drivers: motor: import vibrator driver from CRJ1 package
This commit is contained in:
parent
2897e8b7db
commit
60780e3e6b
|
@ -3752,7 +3752,7 @@ CONFIG_HAVE_CLK_PREPARE=y
|
|||
#
|
||||
# Hardware Spinlock drivers
|
||||
#
|
||||
CONFIG_VIBETONZ=y
|
||||
CONFIG_SS_VIBRATOR=y
|
||||
# CONFIG_TACTILE_ASSIST is not set
|
||||
# CONFIG_VIBRATOR_UPDATE is not set
|
||||
# CONFIG_MOTOR_DRV_ISA1400 is not set
|
||||
|
|
|
@ -3752,7 +3752,7 @@ CONFIG_HAVE_CLK_PREPARE=y
|
|||
#
|
||||
# Hardware Spinlock drivers
|
||||
#
|
||||
CONFIG_VIBETONZ=y
|
||||
CONFIG_SS_VIBRATOR=y
|
||||
# CONFIG_TACTILE_ASSIST is not set
|
||||
# CONFIG_VIBRATOR_UPDATE is not set
|
||||
# CONFIG_MOTOR_DRV_ISA1400 is not set
|
||||
|
|
|
@ -3752,7 +3752,7 @@ CONFIG_HAVE_CLK_PREPARE=y
|
|||
#
|
||||
# Hardware Spinlock drivers
|
||||
#
|
||||
CONFIG_VIBETONZ=y
|
||||
CONFIG_SS_VIBRATOR=y
|
||||
# CONFIG_TACTILE_ASSIST is not set
|
||||
# CONFIG_VIBRATOR_UPDATE is not set
|
||||
# CONFIG_MOTOR_DRV_ISA1400 is not set
|
||||
|
|
|
@ -25,7 +25,7 @@ CONFIG_LEDS_AN30259A=y
|
|||
|
||||
#VIBRATOR
|
||||
#CONFIG_MSM_VIBRATOR=y
|
||||
CONFIG_VIBETONZ=y
|
||||
CONFIG_SS_VIBRATOR=y
|
||||
CONFIG_MOTOR_DRV_DRV2603=y
|
||||
|
||||
#TSP
|
||||
|
|
|
@ -25,7 +25,7 @@ CONFIG_LEDS_AN30259A=y
|
|||
|
||||
#VIBRATOR
|
||||
#CONFIG_MSM_VIBRATOR=y
|
||||
CONFIG_VIBETONZ=y
|
||||
CONFIG_SS_VIBRATOR=y
|
||||
CONFIG_MOTOR_DRV_DRV2603=y
|
||||
|
||||
#TSP
|
||||
|
|
|
@ -144,13 +144,7 @@ obj-$(CONFIG_CORESIGHT) += coresight/
|
|||
obj-$(CONFIG_BIF) += bif/
|
||||
obj-$(CONFIG_BATTERY_SAMSUNG) += battery/
|
||||
obj-$(CONFIG_SENSORS) += sensors/
|
||||
obj-$(CONFIG_MOTOR_DRV_ISA1400) += motor/
|
||||
obj-$(CONFIG_MOTOR_DRV_DRV2603) += motor/
|
||||
obj-$(CONFIG_VIBETONZ) += motor/
|
||||
obj-$(CONFIG_MSM_VIBRATOR) += motor/
|
||||
obj-$(CONFIG_HAPTIC_ISA1200) +=motor/
|
||||
obj-$(CONFIG_MAX77826_VIBRATOR) += motor/
|
||||
obj-$(CONFIG_DRV2604_VIBRATOR) +=motor/
|
||||
obj-y +=motor/
|
||||
|
||||
obj-$(CONFIG_FELICA) += felica/
|
||||
|
||||
|
|
|
@ -1,275 +0,0 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** ImmVibeSPI_isa1200.c
|
||||
**
|
||||
** Description:
|
||||
** Device-dependent functions called by Immersion TSP API
|
||||
** to control PWM duty cycle, amp enable/disable, save IVT file, etc...
|
||||
**
|
||||
** Portions Copyright (c) 2008-2010 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
|
||||
** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** =========================================================================
|
||||
*/
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include "tspdrv_isa1200.h"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/pmic8058.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
|
||||
#ifdef IMMVIBESPIAPI
|
||||
#undef IMMVIBESPIAPI
|
||||
#endif
|
||||
#define IMMVIBESPIAPI static
|
||||
|
||||
/*
|
||||
** This SPI supports only one actuator.
|
||||
*/
|
||||
#define NUM_ACTUATORS 1
|
||||
|
||||
#define HCTRL0 (0x30) /* 0x09 */ /* Haptic Motor Driver Control Register Group 0*/
|
||||
#define HCTRL1 (0x31) /* 0x4B */ /* Haptic Motor Driver Control Register Group 1*/
|
||||
|
||||
static bool g_bAmpEnabled = false;
|
||||
|
||||
int32_t vibe_set_pwm_freq(int nForce)
|
||||
{
|
||||
/* Put the MND counter in reset mode for programming */
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_SEL_VAL_BMSK,0 << HWIO_GP_SRC_SEL_VAL_SHFT); //SRC_SEL = 000(cxo)
|
||||
#if defined (CONFIG_MACH_MATISSELTE_ATT)
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_DIV_VAL_BMSK,23 << HWIO_GP_SRC_DIV_VAL_SHFT); //SRC_DIV = 11111 (Div 12)
|
||||
#else
|
||||
#if defined (CONFIG_MACH_MATISSE3G_OPEN) || defined (CONFIG_SEC_MATISSELTE_COMMON)
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_DIV_VAL_BMSK,29 << HWIO_GP_SRC_DIV_VAL_SHFT); //SRC_DIV = 11111 (Div 16)
|
||||
#else
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_DIV_VAL_BMSK,31 << HWIO_GP_SRC_DIV_VAL_SHFT); //SRC_DIV = 11111 (Div 16)
|
||||
#endif
|
||||
#endif//CONFIG_MACH_MATISSELTE_ATT
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_MODE_VAL_BMSK,2 << HWIO_GP_MODE_VAL_SHFT); //Mode Select 10
|
||||
//M value
|
||||
HWIO_OUTM(GP_M_REG, HWIO_GP_MD_REG_M_VAL_BMSK,g_nlra_gp_clk_m << HWIO_GP_MD_REG_M_VAL_SHFT);
|
||||
if (nForce > 0){
|
||||
g_nforce_32 = g_nlra_gp_clk_n - (((nForce * g_nlra_gp_clk_pwm_mul) >> 8));
|
||||
g_nforce_32 = g_nforce_32 * motor_strength /100;
|
||||
if(g_nforce_32 < motor_min_strength)
|
||||
g_nforce_32 = motor_min_strength;
|
||||
}
|
||||
else {
|
||||
g_nforce_32 = ((nForce * g_nlra_gp_clk_pwm_mul) >> 8) + g_nlra_gp_clk_d;
|
||||
if(g_nlra_gp_clk_n - g_nforce_32 > g_nlra_gp_clk_n * motor_strength /100)
|
||||
g_nforce_32 = g_nlra_gp_clk_n - g_nlra_gp_clk_n * motor_strength /100;
|
||||
}
|
||||
// D value
|
||||
HWIO_OUTM(GP_D_REG, HWIO_GP_MD_REG_D_VAL_BMSK,(~((int16_t)g_nforce_32 << 1)) << HWIO_GP_MD_REG_D_VAL_SHFT);
|
||||
//N value
|
||||
HWIO_OUTM(GP_NS_REG, HWIO_GP_NS_REG_GP_N_VAL_BMSK,~(g_nlra_gp_clk_n - g_nlra_gp_clk_m) << 0);
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int32_t vibe_pwm_onoff(u8 onoff)
|
||||
{
|
||||
if (onoff) {
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_UPDATE_VAL_BMSK,
|
||||
1 << HWIO_UPDATE_VAL_SHFT);//UPDATE ACTIVE
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_ROOT_EN_VAL_BMSK,
|
||||
1 << HWIO_ROOT_EN_VAL_SHFT);//ROOT_EN
|
||||
HWIO_OUTM(CAMSS_GP1_CBCR, HWIO_CLK_ENABLE_VAL_BMSK,
|
||||
1 << HWIO_CLK_ENABLE_VAL_SHFT); //CLK_ENABLE
|
||||
} else {
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_UPDATE_VAL_BMSK,
|
||||
0 << HWIO_UPDATE_VAL_SHFT);
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_ROOT_EN_VAL_BMSK,
|
||||
0 << HWIO_ROOT_EN_VAL_SHFT);
|
||||
HWIO_OUTM(CAMSS_GP1_CBCR, HWIO_CLK_ENABLE_VAL_BMSK,
|
||||
0 << HWIO_CLK_ENABLE_VAL_SHFT);
|
||||
}
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
int vib_isa1200_onoff(u8 onoff)
|
||||
{
|
||||
|
||||
if(onoff) {
|
||||
vibrator_write_register(HCTRL0, 0x88); //HCTRL0
|
||||
vibrator_write_register(HCTRL1, 0x4B);
|
||||
} else {
|
||||
vibrator_write_register(HCTRL0, 0x08);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called to disable amp (disable output force)
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpDisable(VibeUInt8 nActuatorIndex)
|
||||
{
|
||||
if (g_bAmpEnabled)
|
||||
{
|
||||
printk("[VIBETONZ] %s \n",__func__);
|
||||
g_bAmpEnabled = false;
|
||||
vib_isa1200_onoff(0);
|
||||
#if defined (CONFIG_MACH_MATISSE3G_OPEN) || defined (CONFIG_SEC_MATISSELTE_COMMON) || defined (CONFIG_MACH_T10_3G_OPEN)
|
||||
vibrator_drvdata.power_onoff(0);
|
||||
#endif
|
||||
gpio_set_value(vibrator_drvdata.motor_en, VIBRATION_OFF);
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_clk, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),GPIO_CFG_ENABLE);
|
||||
gpio_set_value(vibrator_drvdata.vib_clk, VIBRATION_OFF);
|
||||
}
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called to enable amp (enable output force)
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpEnable(VibeUInt8 nActuatorIndex)
|
||||
{
|
||||
|
||||
|
||||
if (!g_bAmpEnabled)
|
||||
{
|
||||
printk("[VIBETONZ] %s \n",__func__);
|
||||
g_bAmpEnabled = true;
|
||||
|
||||
#if defined (CONFIG_MACH_MATISSE3G_OPEN) || defined (CONFIG_SEC_MATISSELTE_COMMON) || defined (CONFIG_MACH_T10_3G_OPEN)
|
||||
vibrator_drvdata.power_onoff(1);
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_clk, 3, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),GPIO_CFG_ENABLE);
|
||||
#else
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_clk, 6, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),GPIO_CFG_ENABLE);
|
||||
#endif
|
||||
gpio_set_value(vibrator_drvdata.vib_clk, VIBRATION_ON);
|
||||
gpio_set_value(vibrator_drvdata.motor_en, VIBRATION_ON);
|
||||
#if defined (CONFIG_MACH_MATISSE3G_OPEN) || defined (CONFIG_SEC_MATISSELTE_COMMON)
|
||||
msleep(1);
|
||||
#endif
|
||||
vib_isa1200_onoff(1);
|
||||
}
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called at initialization time to set PWM freq, disable amp, etc...
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_Initialize(void)
|
||||
{
|
||||
g_bAmpEnabled = true;
|
||||
DbgOut((KERN_DEBUG "ImmVibeSPI_ForceOut_Initialize.\n"));
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_clk,0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),GPIO_CFG_ENABLE);
|
||||
msleep(1);
|
||||
vibrator_write_register(HCTRL0, 0x08);
|
||||
vibrator_write_register(HCTRL1, 0x4B);
|
||||
ImmVibeSPI_ForceOut_AmpDisable(0);
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called at termination time to set PWM freq, disable amp, etc...
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_Terminate(void)
|
||||
{
|
||||
DbgOut((KERN_DEBUG "ImmVibeSPI_ForceOut_Terminate.\n"));
|
||||
|
||||
/*** Disable amp.
|
||||
** If multiple actuators are supported, please make sure to call
|
||||
** ImmVibeSPI_ForceOut_AmpDisable for each actuator (provide the actuator index as
|
||||
** input argument).
|
||||
*/
|
||||
ImmVibeSPI_ForceOut_AmpDisable(0);
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VIBRATOR_UPDATE
|
||||
static bool g_bOutputDataBufferEmpty = 1;
|
||||
#endif
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_SetSamples(u_int8_t nActuatorIndex,u_int16_t nOutputSignalBitDepth,u_int16_t nBufferSizeInBytes,int8_t *pForceOutputBuffer)
|
||||
{
|
||||
int8_t nforce;
|
||||
static int8_t pre_nforce;
|
||||
#ifdef CONFIG_VIBRATOR_UPDATE
|
||||
if (g_bOutputDataBufferEmpty) {
|
||||
nActuatorIndex = 0;
|
||||
nOutputSignalBitDepth = 8;
|
||||
nBufferSizeInBytes = 1;
|
||||
pForceOutputBuffer[0] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (nOutputSignalBitDepth) {
|
||||
case 8:
|
||||
/* pForceOutputBuffer is expected to contain 1 byte */
|
||||
if (nBufferSizeInBytes != 1) {
|
||||
DbgOut((KERN_ERR"[ImmVibeSPI] ImmVibeSPI_ForceOut_SetSamples nBufferSizeInBytes = %d\n",nBufferSizeInBytes));
|
||||
return VIBE_E_FAIL;
|
||||
}
|
||||
nforce = pForceOutputBuffer[0];
|
||||
break;
|
||||
case 16:
|
||||
/* pForceOutputBuffer is expected to contain 2 byte */
|
||||
if (nBufferSizeInBytes != 2)
|
||||
return VIBE_E_FAIL;
|
||||
|
||||
/* Map 16-bit value to 8-bit */
|
||||
nforce = ((int16_t *)pForceOutputBuffer)[0] >> 8;
|
||||
break;
|
||||
default:
|
||||
/* Unexpected bit depth */
|
||||
return VIBE_E_FAIL;
|
||||
}
|
||||
if (nforce == 0) {
|
||||
/* Set 50% duty cycle or disable amp */
|
||||
ImmVibeSPI_ForceOut_AmpDisable(0);
|
||||
vibe_pwm_onoff(0);
|
||||
nforce = 0;
|
||||
pre_nforce = 0;
|
||||
} else {
|
||||
if (nforce > 0)
|
||||
nforce = 127 - nforce;
|
||||
/* Map force from [-127, 127] to [0, PWM_DUTY_MAX] */
|
||||
/* printk(KERN_DEBUG "[tspdrv]nForce===%d\n", nforce); */
|
||||
if (pre_nforce != nforce) {
|
||||
vibe_set_pwm_freq(nforce);
|
||||
vibe_pwm_onoff(1);
|
||||
ImmVibeSPI_ForceOut_AmpEnable(0);
|
||||
pre_nforce = nforce;
|
||||
}
|
||||
}
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Called to get the device name (device name must be returned as ANSI char)
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_Device_GetName(VibeUInt8 nActuatorIndex, char *szDevName, int nSize)
|
||||
{
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
|
@ -1,237 +0,0 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** ImmVibeSPI.c
|
||||
**
|
||||
** Description:
|
||||
** Device-dependent functions called by Immersion TSP API
|
||||
** to control PWM duty cycle, amp enable/disable, save IVT file, etc...
|
||||
**
|
||||
** Portions Copyright (c) 2008-2010 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
|
||||
** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** =========================================================================
|
||||
*/
|
||||
|
||||
#ifdef IMMVIBESPIAPI
|
||||
#undef IMMVIBESPIAPI
|
||||
#endif
|
||||
#define IMMVIBESPIAPI static
|
||||
|
||||
#include <linux/isa1400_vibrator.h>
|
||||
|
||||
/*
|
||||
** This SPI supports only one actuator.
|
||||
*/
|
||||
#define NUM_ACTUATORS 1
|
||||
|
||||
#define ISA1400_I2C_ADDRESS 0x90
|
||||
|
||||
static bool g_bAmpEnabled[NUM_ACTUATORS];
|
||||
|
||||
/* Variable defined to allow for tuning of the handset. */
|
||||
/* For temporary section for Tuning Work */
|
||||
#if 0
|
||||
#define VIBETONZ_TUNING
|
||||
#define ISA1400_GEN_MODE
|
||||
#endif
|
||||
#define MOTOR_TYPE_LRA
|
||||
|
||||
#ifdef VIBETONZ_TUNING
|
||||
extern VibeStatus ImmVibeGetDeviceKernelParameter(VibeInt32 nDeviceIndex, VibeInt32 nDeviceKernelParamID, VibeInt32 *pnDeviceKernelParamValue);
|
||||
#endif /* VIBETONZ_TUNING */
|
||||
|
||||
/* PWM mode selection */
|
||||
#ifndef ISA1400_GEN_MODE
|
||||
#define ISA1400_PWM_MODE
|
||||
#if 0
|
||||
#define ISA1400_PWM_256DIV_MODE
|
||||
#else
|
||||
#define ISA1400_PWM_128DIV_MODE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Actuator selection */
|
||||
#ifndef MOTOR_TYPE_LRA
|
||||
#define MOTOR_TYPE_ERM
|
||||
#endif
|
||||
|
||||
#define ISA1400_HEN_ENABLE
|
||||
#define RETRY_CNT 10
|
||||
|
||||
#define SYS_API_LEN_HIGH isa1400_chip_enable(true)
|
||||
#define SYS_API_LEN_LOW isa1400_chip_enable(false)
|
||||
#define SYS_API_HEN_HIGH
|
||||
#define SYS_API_HEN_LOW
|
||||
#define SYS_API_VDDP_ON
|
||||
#define SYS_API_VDDP_OFF
|
||||
#define SYS_API__I2C__Write( _addr, _dataLength, _data) \
|
||||
isa1400_i2c_write(_addr, _dataLength, _data)
|
||||
#define SLEEP(_us_time)
|
||||
|
||||
#define PWM_CLK_ENABLE
|
||||
#define PWM_CLK_DISABLE
|
||||
|
||||
#define DEBUG_MSG(fmt, ...) \
|
||||
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
|
||||
|
||||
#define SYS_API_SET_PWM_FREQ(freq)
|
||||
#define SYS_API_SET_PWM_DUTY(index, ratio) isa1400_clk_config(index, ratio)
|
||||
|
||||
#define DEVICE_NAME "Generic"
|
||||
|
||||
/*
|
||||
** Called to disable amp (disable output force)
|
||||
*/
|
||||
extern int vib_ioctl_lock;
|
||||
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpDisable(VibeUInt8 nActuatorIndex)
|
||||
{
|
||||
|
||||
vib_ioctl_lock = 0;
|
||||
if (g_bAmpEnabled[nActuatorIndex])
|
||||
{
|
||||
DbgOut((KERN_DEBUG "ImmVibeSPI_ForceOut_AmpDisable.\n"));
|
||||
|
||||
g_bAmpEnabled[nActuatorIndex] = false;
|
||||
|
||||
}
|
||||
|
||||
if((!g_bAmpEnabled[0]))
|
||||
SYS_API_LEN_LOW;
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called to enable amp (enable output force)
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpEnable(VibeUInt8 nActuatorIndex)
|
||||
{
|
||||
vib_ioctl_lock = 1;
|
||||
if (!g_bAmpEnabled[nActuatorIndex])
|
||||
{
|
||||
DbgOut((KERN_DEBUG "ImmVibeSPI_ForceOut_AmpEnable.\n"));
|
||||
g_bAmpEnabled[nActuatorIndex] = true;
|
||||
|
||||
/* mz_ops.bstat |= HN_BATTERY_MOTOR; */
|
||||
|
||||
}
|
||||
|
||||
if(g_bAmpEnabled[0] )
|
||||
SYS_API_LEN_HIGH;
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
#if 0
|
||||
/*
|
||||
** Called at initialization time to set PWM frequencies, disable amp, etc...
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_Initialize(void)
|
||||
{
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called at termination time to set PWM freq, disable amp, etc...
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_Terminate(void)
|
||||
{
|
||||
|
||||
VibeStatus nActuatorIndex; /* Initialized below. */
|
||||
|
||||
DbgOut((KERN_DEBUG "ImmVibeSPI_ForceOut_Terminate.\n"));
|
||||
|
||||
/* For each actuator... */
|
||||
for (nActuatorIndex = 0; NUM_ACTUATORS > nActuatorIndex; ++nActuatorIndex)
|
||||
{
|
||||
/* Disable amp */
|
||||
ImmVibeSPI_ForceOut_AmpDisable(nActuatorIndex);
|
||||
}
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
** Called by the real-time loop to set PWM_MAG duty cycle
|
||||
*/
|
||||
#ifdef CONFIG_VIBRATOR_UPDATE
|
||||
static bool g_bOutputDataBufferEmpty = 1;
|
||||
#endif
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_SetSamples(VibeUInt8 nActuatorIndex, VibeUInt16 nOutputSignalBitDepth, VibeUInt16 nBufferSizeInBytes, VibeInt8* pForceOutputBuffer)
|
||||
{
|
||||
VibeInt8 nForce = 0;
|
||||
VibeBool bSingleValueOutput = false;
|
||||
//int cnt = 0;
|
||||
//unsigned char I2C_data[2];
|
||||
//int ret = VIBE_S_SUCCESS;
|
||||
|
||||
#ifdef CONFIG_VIBRATOR_UPDATE
|
||||
if (g_bOutputDataBufferEmpty) {
|
||||
nActuatorIndex = 0;
|
||||
nOutputSignalBitDepth = 8;
|
||||
nBufferSizeInBytes = 1;
|
||||
pForceOutputBuffer[0] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (nOutputSignalBitDepth)
|
||||
{
|
||||
case 8:
|
||||
/* For ERM/LRA, pForceOutputBuffer is expected to contain 1 byte */
|
||||
if (nBufferSizeInBytes == 1)
|
||||
{
|
||||
bSingleValueOutput = true;
|
||||
nForce = pForceOutputBuffer[0];
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
/* For ERM/LRA, pForceOutputBuffer is expected to contain 2 byte */
|
||||
if (nBufferSizeInBytes == 2)
|
||||
{
|
||||
bSingleValueOutput = true;
|
||||
/* Map 16-bit value to 8-bit */
|
||||
nForce = ((VibeInt16*)pForceOutputBuffer)[0] >> 8;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Unexpected bit depth */
|
||||
return VIBE_E_FAIL;
|
||||
}
|
||||
|
||||
SYS_API_SET_PWM_DUTY(nActuatorIndex, nForce);
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
#if 0
|
||||
/*
|
||||
** Called to set force output frequency parameters
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_SetFrequency(VibeUInt8 nActuatorIndex, VibeUInt16 nFrequencyParameterID, VibeUInt32 nFrequencyParameterValue)
|
||||
{
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Called to get the device name (device name must be returned as ANSI char)
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_Device_GetName(VibeUInt8 nActuatorIndex, char *szDevName, int nSize)
|
||||
{
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
|
@ -1,434 +0,0 @@
|
|||
/*
|
||||
** =============================================================================
|
||||
**
|
||||
** File:Immvibespi_drv2604.c
|
||||
**
|
||||
** Description:
|
||||
** Device-dependent functions called by Immersion TSP API
|
||||
** to control PWM duty cycle, amp enable/disable, save IVT file, etc...
|
||||
**
|
||||
**
|
||||
** Copyright (c) 2012-2013 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
|
||||
** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
**
|
||||
** =============================================================================
|
||||
*/
|
||||
//#warning ********* Compiling SPI for DRV2604 using LRA actuator ************
|
||||
|
||||
#ifdef IMMVIBESPIAPI
|
||||
#undef IMMVIBESPIAPI
|
||||
#endif
|
||||
#define IMMVIBESPIAPI static
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/cdev.h>
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/syscalls.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <mach/gpiomux.h>
|
||||
|
||||
|
||||
#include <linux/workqueue.h>
|
||||
//#include "tspdrv.h"
|
||||
#include "tspdrv_drv2604.h"
|
||||
|
||||
#ifdef CONFIG_REG_DRV2604L
|
||||
struct dentry *drv_debugfs_dir;
|
||||
#endif
|
||||
|
||||
static int g_nDeviceID = -1;
|
||||
static bool g_bAmpEnabled = false;
|
||||
|
||||
static const unsigned char init_sequence[] = {
|
||||
#include "init.seq"
|
||||
};
|
||||
|
||||
#if SKIP_AUTOCAL == 0
|
||||
static const unsigned char autocal_sequence[] = {
|
||||
#include <autocal.seq>
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef ACTUATOR_NAME
|
||||
#define ACTUATOR_NAME "haptic_motor"
|
||||
#endif
|
||||
|
||||
static void drv2604_write_reg_val(const unsigned char* data, unsigned int size)
|
||||
{
|
||||
|
||||
int i = 0,ret;
|
||||
if (size % 2 != 0)
|
||||
return;
|
||||
|
||||
while (i < size)
|
||||
{
|
||||
ret = i2c_smbus_write_byte_data(vibrator_drvdata.client, data[i], data[i+1]);
|
||||
i+=2;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char drv2604_read_reg(unsigned char reg)
|
||||
{
|
||||
return i2c_smbus_read_byte_data(vibrator_drvdata.client, reg);
|
||||
}
|
||||
|
||||
#if SKIP_AUTOCAL == 0
|
||||
static void drv2604_poll_go_bit(void)
|
||||
{
|
||||
while (drv2604_read_reg(GO_REG) == GO)
|
||||
schedule_timeout_interruptible(msecs_to_jiffies(GO_BIT_POLL_INTERVAL));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void drv2604_set_rtp_val(char value)
|
||||
{
|
||||
char rtp_val[] =
|
||||
{
|
||||
REAL_TIME_PLAYBACK_REG, value
|
||||
};
|
||||
drv2604_write_reg_val(rtp_val, sizeof(rtp_val));
|
||||
}
|
||||
|
||||
static void drv2604_change_mode(char mode)
|
||||
{
|
||||
unsigned char tmp[] =
|
||||
{
|
||||
MODE_REG, mode
|
||||
};
|
||||
drv2604_write_reg_val(tmp, sizeof(tmp));
|
||||
}
|
||||
|
||||
#if USE_DRV2604_EN_PIN
|
||||
static void drv2604_set_en(bool enabled)
|
||||
{
|
||||
gpio_direction_output(vibrator_drvdata.motor_en, enabled ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW);
|
||||
|
||||
if(enabled == true)
|
||||
gpio_set_value(vibrator_drvdata.motor_en,VIBRATION_ON);
|
||||
else
|
||||
gpio_set_value(vibrator_drvdata.motor_en,VIBRATION_OFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_REG_DRV2604L
|
||||
static int drv_debug_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
filp->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t drv_debug_read(struct file *filp,
|
||||
char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
char *buf;
|
||||
size_t len = 0;
|
||||
ssize_t ret;
|
||||
int i;
|
||||
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
|
||||
if (count < sizeof(buf))
|
||||
return -ENOSPC;
|
||||
|
||||
buf = kzalloc(TSPDRV_BUF_SIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
for(i = 0x00; i <= 0xFF; i += 0x01){
|
||||
if(( 0x00 <= i && i <= 0x10) || (0x16 <= i && i <= 0x22) || (0xFD <= i && i<=0xFF)){
|
||||
len += snprintf(buf + len, TSPDRV_BUF_SIZE - len,
|
||||
"drv2604 reg addr = %x , value =%x\n", i, drv2604_read_reg(i));
|
||||
}
|
||||
}
|
||||
ret = simple_read_from_buffer(buffer, len, ppos, buf, TSPDRV_BUF_SIZE);
|
||||
kfree(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations drv_debugfs_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drv_debug_open,
|
||||
.read = drv_debug_read,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int32_t ImmVibeSPI_ForceOut_AmpDisable(u_int8_t nActuatorIndex);
|
||||
|
||||
#if GUARANTEE_AUTOTUNE_BRAKE_TIME
|
||||
|
||||
#define AUTOTUNE_BRAKE_TIME 25
|
||||
|
||||
static VibeInt8 g_lastForce = 0;
|
||||
static bool g_brake = false;
|
||||
|
||||
static void autotune_brake_complete(struct work_struct *work)
|
||||
{
|
||||
/* new nForce value came in before workqueue terminated */
|
||||
if (g_lastForce > 0)
|
||||
return;
|
||||
|
||||
#if USE_DRV2604_STANDBY
|
||||
/* Put hardware in standby */
|
||||
drv2604_change_mode(MODE_STANDBY);
|
||||
#endif
|
||||
|
||||
#if USE_DRV2604_EN_PIN
|
||||
drv2604_set_en(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
DECLARE_DELAYED_WORK(g_brake_complete, autotune_brake_complete);
|
||||
|
||||
static struct workqueue_struct *g_workqueue;
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Called to disable amp (disable output force)
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpDisable(VibeUInt8 nActuatorIndex)
|
||||
{
|
||||
if (g_bAmpEnabled)
|
||||
{
|
||||
/* Set the force to 0 */
|
||||
drv2604_set_rtp_val(0);
|
||||
|
||||
#if GUARANTEE_AUTOTUNE_BRAKE_TIME
|
||||
/* if a brake signal arrived from daemon, let the chip stay on
|
||||
* extra time to allow it to brake */
|
||||
if (g_brake && g_workqueue)
|
||||
{
|
||||
queue_delayed_work(g_workqueue,
|
||||
&g_brake_complete,
|
||||
msecs_to_jiffies(AUTOTUNE_BRAKE_TIME));
|
||||
}
|
||||
else /* disable immediately (smooth effect style) */
|
||||
#endif
|
||||
{
|
||||
#if USE_DRV2604_STANDBY
|
||||
/* Put hardware in standby via i2c */
|
||||
drv2604_change_mode(MODE_STANDBY);
|
||||
#endif
|
||||
|
||||
#if USE_DRV2604_EN_PIN
|
||||
/* Disable hardware via pin */
|
||||
drv2604_set_en(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
g_bAmpEnabled = false;
|
||||
}
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called to enable amp (enable output force)
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpEnable(VibeUInt8 nActuatorIndex)
|
||||
{
|
||||
if (!g_bAmpEnabled)
|
||||
{
|
||||
#if GUARANTEE_AUTOTUNE_BRAKE_TIME
|
||||
cancel_delayed_work_sync(&g_brake_complete);
|
||||
#endif
|
||||
|
||||
#if USE_DRV2604_EN_PIN
|
||||
drv2604_set_en(true);
|
||||
#endif
|
||||
|
||||
#if USE_DRV2604_STANDBY
|
||||
drv2604_change_mode(MODE_DEVICE_READY);
|
||||
#endif
|
||||
// Chip requires minimum 250us power-up delay before RTP value can be set
|
||||
// If the chip is powered on <10ms after powering off, it needs 1000us
|
||||
// for the internal LDO voltage to stabilize
|
||||
usleep_range(1000, 1000);
|
||||
|
||||
/* Workaround for power issue in the DRV2604 */
|
||||
/* Restore the register settings if they have reset to the defaults */
|
||||
|
||||
if(drv2604_read_reg(RATED_VOLTAGE_REG) != init_sequence[3])
|
||||
{
|
||||
drv2604_write_reg_val(init_sequence, sizeof(init_sequence));
|
||||
}
|
||||
|
||||
drv2604_change_mode(MODE_REAL_TIME_PLAYBACK);
|
||||
g_bAmpEnabled = true;
|
||||
}
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called at initialization time.
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_Initialize(void)
|
||||
{
|
||||
|
||||
int retVal=0;
|
||||
if (retVal) {
|
||||
return VIBE_E_FAIL;
|
||||
}
|
||||
|
||||
#if GUARANTEE_AUTOTUNE_BRAKE_TIME
|
||||
g_workqueue = create_workqueue("tspdrv_workqueue");
|
||||
#endif
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called at termination time to disable amp, etc...
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_Terminate(void)
|
||||
{
|
||||
|
||||
#if GUARANTEE_AUTOTUNE_BRAKE_TIME
|
||||
if (g_workqueue)
|
||||
{
|
||||
destroy_workqueue(g_workqueue);
|
||||
g_workqueue = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
ImmVibeSPI_ForceOut_AmpDisable(0);
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called by the real-time loop to set the force
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_SetSamples(VibeUInt8 nActuatorIndex, VibeUInt16 nOutputSignalBitDepth, VibeUInt16 nBufferSizeInBytes, VibeInt8* pForceOutputBuffer)
|
||||
{
|
||||
|
||||
#if GUARANTEE_AUTOTUNE_BRAKE_TIME
|
||||
VibeInt8 force = pForceOutputBuffer[0];
|
||||
if (force > 0 && g_lastForce <= 0)
|
||||
{
|
||||
g_brake = false;
|
||||
|
||||
ImmVibeSPI_ForceOut_AmpEnable(nActuatorIndex);
|
||||
}
|
||||
else if (force <= 0 && g_lastForce > 0)
|
||||
{
|
||||
g_brake = force < 0;
|
||||
|
||||
ImmVibeSPI_ForceOut_AmpDisable(nActuatorIndex);
|
||||
}
|
||||
|
||||
if (g_lastForce != force)
|
||||
{
|
||||
/* AmpDisable sets force to zero, so need to here */
|
||||
if (force > 0)
|
||||
drv2604_set_rtp_val(pForceOutputBuffer[0]);
|
||||
|
||||
g_lastForce = force;
|
||||
}
|
||||
#else
|
||||
drv2604_set_rtp_val(pForceOutputBuffer[0]);
|
||||
#endif
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called to set force output frequency parameters
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_SetFrequency(VibeUInt8 nActuatorIndex, VibeUInt16 nFrequencyParameterID, VibeUInt32 nFrequencyParameterValue)
|
||||
{
|
||||
if (nActuatorIndex != 0) return VIBE_S_SUCCESS;
|
||||
|
||||
switch (nFrequencyParameterID)
|
||||
{
|
||||
case VIBE_KP_CFG_FREQUENCY_PARAM1:
|
||||
break;
|
||||
|
||||
case VIBE_KP_CFG_FREQUENCY_PARAM2:
|
||||
break;
|
||||
|
||||
case VIBE_KP_CFG_FREQUENCY_PARAM3:
|
||||
break;
|
||||
|
||||
case VIBE_KP_CFG_FREQUENCY_PARAM4:
|
||||
break;
|
||||
|
||||
case VIBE_KP_CFG_FREQUENCY_PARAM5:
|
||||
break;
|
||||
|
||||
case VIBE_KP_CFG_FREQUENCY_PARAM6:
|
||||
break;
|
||||
}
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called to get the device name (device name must be returned as ANSI char)
|
||||
*/
|
||||
IMMVIBESPIAPI VibeStatus ImmVibeSPI_Device_GetName(VibeUInt8 nActuatorIndex, char *szDevName, int nSize)
|
||||
{
|
||||
char szRevision[MAX_REVISION_STRING_SIZE];
|
||||
|
||||
if ((!szDevName) || (nSize < 1)) return VIBE_E_FAIL;
|
||||
|
||||
switch (g_nDeviceID)
|
||||
{
|
||||
case DRV2605:
|
||||
strncpy(szDevName, "DRV2605", nSize-1);
|
||||
break;
|
||||
case DRV2604:
|
||||
strncpy(szDevName, "DRV2604", nSize-1);
|
||||
case DRV2604L:
|
||||
strncpy(szDevName, "DRV2604L", nSize-1);
|
||||
break;
|
||||
case DRV2605L:
|
||||
strncpy(szDevName, "DRV2605L", nSize-1);
|
||||
default:
|
||||
strncpy(szDevName, "Unknown", nSize-1);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Append revision number to the device name */
|
||||
sprintf(szRevision, "r%d %s", (drv2604_read_reg(SILICON_REVISION_REG) & SILICON_REVISION_MASK), ACTUATOR_NAME);
|
||||
if ((strlen(szRevision) + strlen(szDevName)) < nSize-1)
|
||||
strcat(szDevName, szRevision);
|
||||
|
||||
szDevName[nSize - 1] = '\0'; /* make sure the string is NULL terminated */
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,23 +1,8 @@
|
|||
#
|
||||
# Immersion TouchSense Player driver configuration
|
||||
#
|
||||
menuconfig VIBETONZ
|
||||
tristate "Vibetonz"
|
||||
menuconfig SS_VIBRATOR
|
||||
tristate "SS_VIBRATOR"
|
||||
default n
|
||||
help
|
||||
Say Y to enable Vibetonz support.
|
||||
-------------
|
||||
-------------
|
||||
-------------
|
||||
|
||||
config TACTILE_ASSIST
|
||||
tristate "Auto Haptic"
|
||||
default n
|
||||
depends on VIBETONZ
|
||||
|
||||
config VIBRATOR_UPDATE
|
||||
tristate "Immersion Update"
|
||||
default n
|
||||
Say Y to enable SAMSUNG_VIBRATOR support.
|
||||
|
||||
config MOTOR_DRV_MAX77803
|
||||
tristate "Maxim MAX77803 motor"
|
||||
|
@ -54,24 +39,10 @@ config MOTOR_DRV_DRV2603
|
|||
help
|
||||
Say Y to enalbe the DRV2603 IC.
|
||||
|
||||
config DRV2604_VIBRATOR
|
||||
tristate "DRV2604_VIBRATOR"
|
||||
default n
|
||||
help
|
||||
Say Y to enable DRV2604_VIBRATOR support.
|
||||
|
||||
config MOTOR_ISA1000
|
||||
tristate "ISA1000 motor"
|
||||
default n
|
||||
depends on VIBETONZ
|
||||
#
|
||||
# Immersion TouchSense Player driver configuration
|
||||
#
|
||||
menuconfig MOTOR_DRV_TSP5000
|
||||
tristate "MOTOR_DRV_TSP5000"
|
||||
default n
|
||||
help
|
||||
Say Y to enable MOTOR_DRV_TSP5000 support.
|
||||
depends on SS_VIBRATOR
|
||||
|
||||
#
|
||||
# vibrator controlled by PMIC regualtor configuration
|
||||
|
@ -89,8 +60,8 @@ menuconfig MAX77826_VIBRATOR
|
|||
Say Y to enable MAX77826_VIBRATOR support.
|
||||
|
||||
config CONFIG_HAPTIC_ISA1200
|
||||
tristate "ISA1200 motor"
|
||||
default n
|
||||
depends on I2C
|
||||
help
|
||||
Say Y to enable the ISA1200 IC.
|
||||
tristate "ISA1200 motor"
|
||||
default n
|
||||
depends on I2C
|
||||
help
|
||||
Say Y to enable the ISA1200 IC.
|
||||
|
|
|
@ -1,21 +1,14 @@
|
|||
#
|
||||
# makefile for tspdrv kernel module
|
||||
#
|
||||
|
||||
obj-$(CONFIG_VIBETONZ) += vibrator.o
|
||||
obj-$(CONFIG_MOTOR_DRV_MAX77803) += max77803_haptic.o
|
||||
obj-$(CONFIG_MOTOR_DRV_MAX77804K) += max77804k_haptic.o
|
||||
obj-$(CONFIG_MOTOR_DRV_MAX77828) += max77828_haptic.o
|
||||
obj-$(CONFIG_MOTOR_DRV_MAX77888) += max77888_haptic.o
|
||||
obj-$(CONFIG_MOTOR_DRV_ISA1400) += isa1400_vibrator.o
|
||||
obj-$(CONFIG_MOTOR_DRV_TSP5000) += tspdrv_TSP5000.o
|
||||
obj-$(CONFIG_HAPTIC_ISA1200) += tspdrv_isa1200.o
|
||||
obj-$(CONFIG_DRV2604_VIBRATOR) += tspdrv_drv2604.o
|
||||
|
||||
vibrator-objs +=tspdrv.o
|
||||
obj-$(CONFIG_HAPTIC_ISA1200) += isa1200_vibrator.o
|
||||
|
||||
# Makefile for Vibrator controlled by PMIC Regulator.
|
||||
obj-$(CONFIG_MSM_VIBRATOR) += msm_vibrator.o
|
||||
|
||||
obj-$(CONFIG_SS_VIBRATOR) += ss_vibrator.o
|
||||
|
||||
# Makefile for Vibrator controlled by SUB PMIC Regulator.
|
||||
obj-$(CONFIG_MAX77826_VIBRATOR) += max77826_vibrator.o
|
||||
|
|
|
@ -1,262 +0,0 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** VibeOSKernelLinuxHRTime.c
|
||||
**
|
||||
** Description:
|
||||
** High Resolution Time helper functions for Linux.
|
||||
**
|
||||
** Portions Copyright (c) 2010 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
|
||||
** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** =========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** Kernel high-resolution software timer is used as an example but another type
|
||||
** of timer (such as HW timer or standard software timer)
|
||||
** might be used to achieve the 5ms required rate.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_HIGH_RES_TIMERS
|
||||
#warning "The Kernel does not have high resolution timers enabled."\
|
||||
"Either provide a non hr-timer implementation of VibeOSKernelLinuxTime."\
|
||||
"c or re-compile your kernel with CONFIG_HIGH_RES_TIMERS=y"
|
||||
#endif
|
||||
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/semaphore.h>
|
||||
#define WATCHDOG_TIMEOUT 10 /* 10 timer cycles = 50ms */
|
||||
|
||||
/* Global variables */
|
||||
static bool g_btimerstarted;
|
||||
static struct hrtimer g_tsptimer;
|
||||
static ktime_t g_ktfivems;
|
||||
static int g_nwatchdog_counter;
|
||||
|
||||
static struct semaphore g_mutex;
|
||||
|
||||
/* Forward declarations */
|
||||
static void VibeOSKernelLinuxStartTimer(void);
|
||||
static void VibeOSKernelLinuxStopTimer(void);
|
||||
static int VibeOSKernelProcessData(void *data);
|
||||
#define VIBEOSKERNELPROCESSDATA
|
||||
|
||||
static inline int VibeSemIsLocked(struct semaphore *lock)
|
||||
{
|
||||
return (lock->count) != 1;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart tsp_timer_interrupt(struct hrtimer *timer)
|
||||
{
|
||||
/* Scheduling next timeout value right away */
|
||||
hrtimer_forward_now(timer, g_ktfivems);
|
||||
|
||||
if (g_btimerstarted)
|
||||
if (VibeSemIsLocked(&g_mutex))
|
||||
up(&g_mutex);
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
static int VibeOSKernelProcessData(void *data)
|
||||
{
|
||||
int i;
|
||||
int nactuator_not_playing = 0;
|
||||
|
||||
for (i = 0; i < NUM_ACTUATORS; i++) {
|
||||
actuator_samples_buffer *pcurrent_actuator_sample =
|
||||
&(g_samples_buffer[i]);
|
||||
|
||||
if (-1 == pcurrent_actuator_sample->nindex_playing_buffer) {
|
||||
nactuator_not_playing++;
|
||||
if ((NUM_ACTUATORS == nactuator_not_playing) &&
|
||||
((++g_nwatchdog_counter) > WATCHDOG_TIMEOUT)) {
|
||||
int8_t czero[1] = {0};
|
||||
|
||||
/*
|
||||
** Nothing to play for all actuators,
|
||||
** turn off the timer
|
||||
** when we reach the watchdog tick count limit
|
||||
*/
|
||||
ImmVibeSPI_ForceOut_SetSamples(i, 8, 1, czero);
|
||||
ImmVibeSPI_ForceOut_AmpDisable(i);
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
|
||||
/* Reset watchdog counter */
|
||||
g_nwatchdog_counter = 0;
|
||||
}
|
||||
} else {
|
||||
/* Play the current buffer */
|
||||
if (VIBE_E_FAIL == ImmVibeSPI_ForceOut_SetSamples(
|
||||
pcurrent_actuator_sample->actuator_samples
|
||||
[(int)pcurrent_actuator_sample->nindex_playing_buffer]
|
||||
.nactuator_index,
|
||||
pcurrent_actuator_sample->actuator_samples
|
||||
[(int)pcurrent_actuator_sample->nindex_playing_buffer]
|
||||
.nbit_depth,
|
||||
pcurrent_actuator_sample->actuator_samples
|
||||
[(int)pcurrent_actuator_sample->nindex_playing_buffer]
|
||||
.nbuffer_size,
|
||||
pcurrent_actuator_sample->actuator_samples
|
||||
[(int)pcurrent_actuator_sample->nindex_playing_buffer]
|
||||
.data_buffer)) {
|
||||
/* VIBE_E_FAIL means NAK has been handled.
|
||||
Schedule timer to restart 5 ms from now */
|
||||
hrtimer_forward_now(&g_tsptimer, g_ktfivems);
|
||||
}
|
||||
|
||||
pcurrent_actuator_sample->nindex_output_value +=
|
||||
pcurrent_actuator_sample->actuator_samples
|
||||
[(int)pcurrent_actuator_sample->nindex_playing_buffer]
|
||||
.nbuffer_size;
|
||||
|
||||
if (pcurrent_actuator_sample->nindex_output_value >=
|
||||
pcurrent_actuator_sample->actuator_samples
|
||||
[(int)pcurrent_actuator_sample
|
||||
->nindex_playing_buffer]
|
||||
.nbuffer_size) {
|
||||
/* Reach the end of the current buffer */
|
||||
pcurrent_actuator_sample->actuator_samples
|
||||
[(int)pcurrent_actuator_sample
|
||||
->nindex_playing_buffer]
|
||||
.nbuffer_size = 0;
|
||||
|
||||
/* Switch buffer */
|
||||
(pcurrent_actuator_sample
|
||||
->nindex_playing_buffer) ^= 1;
|
||||
pcurrent_actuator_sample
|
||||
->nindex_output_value = 0;
|
||||
|
||||
/* Finished playing,
|
||||
disable amp for actuator (i) */
|
||||
if (g_bstoprequested) {
|
||||
pcurrent_actuator_sample
|
||||
->nindex_playing_buffer = -1;
|
||||
|
||||
ImmVibeSPI_ForceOut_AmpDisable(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If finished playing, stop timer */
|
||||
if (g_bstoprequested) {
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
|
||||
#if defined(CONFIG_TACTILE_ASSIST) || defined(CONFIG_VIBRATOR_UPDATE)
|
||||
// stop all actuator
|
||||
for (i = 0; i < NUM_ACTUATORS; i++)
|
||||
{
|
||||
ImmVibeSPI_ForceOut_AmpDisable(i);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Reset watchdog counter */
|
||||
g_nwatchdog_counter = 0;
|
||||
|
||||
if (VibeSemIsLocked(&g_mutex))
|
||||
up(&g_mutex);
|
||||
return 1; /* tell the caller this is the last iteration */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxInitTimer(void)
|
||||
{
|
||||
/* Get a 5,000,000ns = 5ms time value */
|
||||
g_ktfivems = ktime_set(0, 5000000);
|
||||
hrtimer_init(&g_tsptimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
|
||||
/* Initialize a 5ms-timer with tsp_timer_interrupt
|
||||
as timer callback (interrupt driven)*/
|
||||
g_tsptimer.function = tsp_timer_interrupt;
|
||||
sema_init(&g_mutex, 1); /* initialize simaphore */
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxStartTimer(void)
|
||||
{
|
||||
int i;
|
||||
int res;
|
||||
|
||||
/* Reset watchdog counter */
|
||||
g_nwatchdog_counter = 0;
|
||||
|
||||
if (!g_btimerstarted) {
|
||||
if (!VibeSemIsLocked(&g_mutex))
|
||||
res = down_interruptible(&g_mutex); /* start locked */
|
||||
|
||||
g_btimerstarted = true;
|
||||
|
||||
/* Start the timer */
|
||||
hrtimer_start(&g_tsptimer, g_ktfivems, HRTIMER_MODE_REL);
|
||||
|
||||
/* Don't block the write() function after the first sample
|
||||
to allow the host sending the next samples with no delay */
|
||||
for (i = 0; i < NUM_ACTUATORS; i++) {
|
||||
if ((g_samples_buffer[i]
|
||||
.actuator_samples[0].nbuffer_size) ||
|
||||
(g_samples_buffer[i]
|
||||
.actuator_samples[1].nbuffer_size)) {
|
||||
g_samples_buffer[i].nindex_output_value = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != VibeOSKernelProcessData(NULL))
|
||||
return;
|
||||
|
||||
/*
|
||||
** Use interruptible version of down to be safe
|
||||
** (try to not being stuck here if the mutex is
|
||||
** not freed for any reason)
|
||||
*/
|
||||
/* wait for the mutex to be freed by the timer */
|
||||
res = down_interruptible(&g_mutex);
|
||||
if (res != 0)
|
||||
DbgOut((KERN_INFO
|
||||
"tspdrv: down_interruptible interrupted by a signal.\n"));
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxStopTimer(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (g_btimerstarted) {
|
||||
g_btimerstarted = false;
|
||||
hrtimer_cancel(&g_tsptimer);
|
||||
}
|
||||
|
||||
/* Reset samples buffers */
|
||||
for (i = 0; i < NUM_ACTUATORS; i++) {
|
||||
g_samples_buffer[i].nindex_playing_buffer = -1;
|
||||
g_samples_buffer[i].actuator_samples[0].nbuffer_size = 0;
|
||||
g_samples_buffer[i].actuator_samples[1].nbuffer_size = 0;
|
||||
}
|
||||
g_bstoprequested = false;
|
||||
g_bisplaying = false;
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxTerminateTimer(void)
|
||||
{
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
if (VibeSemIsLocked(&g_mutex))
|
||||
up(&g_mutex);
|
||||
}
|
|
@ -1,194 +0,0 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** VibeOSKernelLinuxHRTime_drv2604.c
|
||||
**
|
||||
** Description:
|
||||
** High Resolution Time helper functions for Linux.
|
||||
**
|
||||
** Portions Copyright (c) 2010-2012 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
|
||||
** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** =========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** Kernel high-resolution software timer is used as an example but another type
|
||||
** of timer (such as HW timer or standard software timer) might be used to achieve
|
||||
** the 5ms required rate.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_HIGH_RES_TIMERS
|
||||
#warning "The Kernel does not have high resolution timers enabled. Either provide a non hr-timer implementation of VibeOSKernelLinuxTime.c or re-compile your kernel with CONFIG_HIGH_RES_TIMERS=y"
|
||||
#endif
|
||||
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/wakelock.h>
|
||||
|
||||
#define WATCHDOG_TIMEOUT 10 /* 10 timer cycles */
|
||||
|
||||
/* Global variables */
|
||||
static bool g_bTimerStarted = false;
|
||||
static struct hrtimer g_tspTimer;
|
||||
static ktime_t g_ktTimerPeriod; /* ktime_t equivalent of g_nTimerPeriodMs */
|
||||
static int g_nWatchdogCounter = 0;
|
||||
static struct wake_lock g_tspWakelock;
|
||||
|
||||
#ifndef NUM_EXTRA_BUFFERS
|
||||
#define NUM_EXTRA_BUFFERS 0
|
||||
#endif
|
||||
struct semaphore g_hSemaphore;
|
||||
|
||||
/* Forward declarations */
|
||||
static void VibeOSKernelLinuxStartTimer(void);
|
||||
static void VibeOSKernelLinuxStopTimer(void);
|
||||
|
||||
static inline int VibeSemIsLocked(struct semaphore *lock)
|
||||
{
|
||||
#if ((LINUX_VERSION_CODE & 0xFFFFFF) < KERNEL_VERSION(2,6,27))
|
||||
return atomic_read(&lock->count) < 1;
|
||||
#else
|
||||
return (lock->count) < 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static enum hrtimer_restart VibeOSKernelTimerProc(struct hrtimer *timer)
|
||||
{
|
||||
/* Return right away if timer is not supposed to run */
|
||||
if (!g_bTimerStarted) return HRTIMER_NORESTART;
|
||||
|
||||
/* Scheduling next timeout value right away */
|
||||
if (++g_nWatchdogCounter < WATCHDOG_TIMEOUT)
|
||||
hrtimer_forward_now(timer, g_ktTimerPeriod);
|
||||
|
||||
if (VibeSemIsLocked(&g_hSemaphore))
|
||||
{
|
||||
up(&g_hSemaphore);
|
||||
}
|
||||
|
||||
if (g_nWatchdogCounter < WATCHDOG_TIMEOUT)
|
||||
{
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Do not call SPI functions in this function as their implementation coud use interrupt */
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
}
|
||||
|
||||
static int VibeOSKernelProcessData(void* data)
|
||||
{
|
||||
SendOutputData();
|
||||
|
||||
/* Reset watchdog counter */
|
||||
g_nWatchdogCounter = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxInitTimer(void)
|
||||
{
|
||||
/* Convert the initial timer period in ktime_t value */
|
||||
g_ktTimerPeriod = ktime_set(0, g_nTimerPeriodMs * 1000000);
|
||||
|
||||
hrtimer_init(&g_tspTimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
|
||||
/* Initialize a 5ms-timer with VibeOSKernelTimerProc as timer callback (interrupt driven)*/
|
||||
g_tspTimer.function = VibeOSKernelTimerProc;
|
||||
wake_lock_init(&g_tspWakelock, WAKE_LOCK_SUSPEND, "tspdrv");
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxStartTimer(void)
|
||||
{
|
||||
/* Reset watchdog counter */
|
||||
g_nWatchdogCounter = 0;
|
||||
|
||||
if (!g_bTimerStarted)
|
||||
{
|
||||
wake_lock(&g_tspWakelock);
|
||||
/* (Re-)Initialize the semaphore used with the timer */
|
||||
sema_init(&g_hSemaphore, NUM_EXTRA_BUFFERS);
|
||||
|
||||
g_bTimerStarted = true;
|
||||
|
||||
/* Start the timer */
|
||||
g_ktTimerPeriod = ktime_set(0, g_nTimerPeriodMs * 1000000);
|
||||
hrtimer_start(&g_tspTimer, g_ktTimerPeriod, HRTIMER_MODE_REL);
|
||||
}
|
||||
else
|
||||
{
|
||||
int res;
|
||||
/*
|
||||
** Use interruptible version of down to be safe
|
||||
** (try to not being stuck here if the semaphore is not freed for any reason)
|
||||
*/
|
||||
res = down_interruptible(&g_hSemaphore); /* wait for the semaphore to be freed by the timer */
|
||||
if (res != 0)
|
||||
{
|
||||
DbgOut((DBL_INFO, "VibeOSKernelLinuxStartTimer: down_interruptible interrupted by a signal.\n"));
|
||||
}
|
||||
}
|
||||
VibeOSKernelProcessData(NULL);
|
||||
/*
|
||||
** Because of possible NACK handling, the VibeOSKernelProcessData() call above could take more than
|
||||
** 5 ms on some piezo devices that are buffering output samples; when this happens, the timer
|
||||
** interrupt will release the g_hSemaphore while VibeOSKernelProcessData is executing and the player
|
||||
** will immediately send the new packet to the SPI layer when VibeOSKernelProcessData exits, which
|
||||
** could cause another NACK right away. To avoid that, we'll create a small delay if the semaphore
|
||||
** was released when VibeOSKernelProcessData exits, by acquiring the mutex again and waiting for
|
||||
** the timer to release it.
|
||||
*/
|
||||
#if defined(NUM_EXTRA_BUFFERS) && (NUM_EXTRA_BUFFERS)
|
||||
if (g_bTimerStarted && !VibeSemIsLocked(&g_hSemaphore))
|
||||
{
|
||||
int res;
|
||||
|
||||
res = down_interruptible(&g_hSemaphore);
|
||||
|
||||
if (res != 0)
|
||||
{
|
||||
DbgOut((DBL_INFO, "VibeOSKernelLinuxStartTimer: down_interruptible interrupted by a signal.\n"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxStopTimer(void)
|
||||
{
|
||||
if (g_bTimerStarted)
|
||||
{
|
||||
g_bTimerStarted = false;
|
||||
}
|
||||
|
||||
/* Reset samples buffers */
|
||||
ResetOutputData();
|
||||
|
||||
g_bIsPlaying = false;
|
||||
wake_unlock(&g_tspWakelock);
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxTerminateTimer(void)
|
||||
{
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
hrtimer_cancel(&g_tspTimer);
|
||||
wake_lock_destroy(&g_tspWakelock);
|
||||
|
||||
if (VibeSemIsLocked(&g_hSemaphore)) up(&g_hSemaphore);
|
||||
}
|
|
@ -1,235 +0,0 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** VibeOSKernelLinuxHRTime_isa1200.c
|
||||
**
|
||||
** Description:
|
||||
** High Resolution Time helper functions for Linux.
|
||||
**
|
||||
** Portions Copyright (c) 2010-2011 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** =========================================================================
|
||||
*/
|
||||
|
||||
/*** Kernel high-resolution software timer is used as an example but another type
|
||||
** of timer (such as HW timer or standard software timer) might be used to achieve
|
||||
** the 5ms required rate.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_HIGH_RES_TIMERS
|
||||
#warning "The Kernel does not have high resolution timers enabled. Either provide a non hr-timer implementation of VibeOSKernelLinuxTime.c or re-compile your kernel with CONFIG_HIGH_RES_TIMERS=y"
|
||||
#endif
|
||||
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define WATCHDOG_TIMEOUT 10 /* 10 timer cycles = 50ms */
|
||||
|
||||
/* For compatibility with older Kernels */
|
||||
#ifndef DEFINE_SEMAPHORE
|
||||
#define DEFINE_SEMAPHORE(name) \
|
||||
struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
|
||||
#endif
|
||||
|
||||
/* Global variables */
|
||||
static bool g_bTimerStarted = false;
|
||||
static struct hrtimer g_tspTimer;
|
||||
static ktime_t g_ktFiveMs;
|
||||
static int g_nWatchdogCounter = 0;
|
||||
|
||||
DEFINE_SEMAPHORE(g_hMutex);
|
||||
|
||||
/* Forward declarations */
|
||||
static void VibeOSKernelLinuxStartTimer(void);
|
||||
static void VibeOSKernelLinuxStopTimer(void);
|
||||
static int VibeOSKernelProcessData(void* data);
|
||||
#define VIBEOSKERNELPROCESSDATA
|
||||
|
||||
static inline int VibeSemIsLocked(struct semaphore *lock)
|
||||
{
|
||||
#if ((LINUX_VERSION_CODE & 0xFFFFFF) < KERNEL_VERSION(2,6,27))
|
||||
return atomic_read(&lock->count) != 1;
|
||||
#else
|
||||
return (lock->count) != 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static enum hrtimer_restart tsp_timer_interrupt(struct hrtimer *timer)
|
||||
{
|
||||
/* Scheduling next timeout value right away */
|
||||
hrtimer_forward_now(timer, g_ktFiveMs);
|
||||
|
||||
if(g_bTimerStarted)
|
||||
{
|
||||
if (VibeSemIsLocked(&g_hMutex)) up(&g_hMutex);
|
||||
}
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
static int VibeOSKernelProcessData(void* data)
|
||||
{
|
||||
int i;
|
||||
int nActuatorNotPlaying = 0;
|
||||
|
||||
for (i = 0; i < NUM_ACTUATORS; i++)
|
||||
{
|
||||
actuator_samples_buffer *pCurrentActuatorSample = &(g_SamplesBuffer[i]);
|
||||
|
||||
if (-1 == pCurrentActuatorSample->nIndexPlayingBuffer)
|
||||
{
|
||||
nActuatorNotPlaying++;
|
||||
if ((NUM_ACTUATORS == nActuatorNotPlaying) && ((++g_nWatchdogCounter) > WATCHDOG_TIMEOUT))
|
||||
{
|
||||
VibeInt8 cZero[1] = {0};
|
||||
|
||||
/* Nothing to play for all actuators, turn off the timer when we reach the watchdog tick count limit */
|
||||
ImmVibeSPI_ForceOut_SetSamples(i, 8, 1, cZero);
|
||||
ImmVibeSPI_ForceOut_AmpDisable(i);
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
|
||||
/* Reset watchdog counter */
|
||||
g_nWatchdogCounter = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Play the current buffer */
|
||||
if (VIBE_E_FAIL == ImmVibeSPI_ForceOut_SetSamples(
|
||||
pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nActuatorIndex,
|
||||
pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBitDepth,
|
||||
pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize,
|
||||
pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].dataBuffer))
|
||||
{
|
||||
/* VIBE_E_FAIL means NAK has been handled. Schedule timer to restart 5 ms from now */
|
||||
hrtimer_forward_now(&g_tspTimer, g_ktFiveMs);
|
||||
}
|
||||
|
||||
pCurrentActuatorSample->nIndexOutputValue += pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize;
|
||||
|
||||
if (pCurrentActuatorSample->nIndexOutputValue >= pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize)
|
||||
{
|
||||
/* Reach the end of the current buffer */
|
||||
pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize = 0;
|
||||
|
||||
/* Switch buffer */
|
||||
(pCurrentActuatorSample->nIndexPlayingBuffer) ^= 1;
|
||||
pCurrentActuatorSample->nIndexOutputValue = 0;
|
||||
|
||||
/* Finished playing, disable amp for actuator (i) */
|
||||
if (g_bStopRequested)
|
||||
{
|
||||
pCurrentActuatorSample->nIndexPlayingBuffer = -1;
|
||||
ImmVibeSPI_ForceOut_AmpDisable(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If finished playing, stop timer */
|
||||
if (g_bStopRequested)
|
||||
{
|
||||
VibeOSKernelLinuxStopTimer();
#if defined(CONFIG_VIBRATOR_UPDATE)
// stop all actuator
for (i = 0; i < NUM_ACTUATORS; i++)
{
ImmVibeSPI_ForceOut_AmpDisable(i);
}
#endif
|
||||
|
||||
/* Reset watchdog counter */
|
||||
g_nWatchdogCounter = 0;
|
||||
|
||||
if (VibeSemIsLocked(&g_hMutex)) up(&g_hMutex);
|
||||
return 1; /* tell the caller this is the last iteration */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxInitTimer(void)
|
||||
{
|
||||
/* Get a 5,000,000ns = 5ms time value */
|
||||
g_ktFiveMs = ktime_set(0, 5000000);
|
||||
|
||||
hrtimer_init(&g_tspTimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
|
||||
/* Initialize a 5ms-timer with tsp_timer_interrupt as timer callback (interrupt driven)*/
|
||||
g_tspTimer.function = tsp_timer_interrupt;
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxStartTimer(void)
|
||||
{
|
||||
int i;
|
||||
int res;
|
||||
|
||||
/* Reset watchdog counter */
|
||||
g_nWatchdogCounter = 0;
|
||||
|
||||
if (!g_bTimerStarted)
|
||||
{
|
||||
if (!VibeSemIsLocked(&g_hMutex)) res = down_interruptible(&g_hMutex); /* start locked */
|
||||
|
||||
g_bTimerStarted = true;
|
||||
|
||||
/* Start the timer */
|
||||
hrtimer_start(&g_tspTimer, g_ktFiveMs, HRTIMER_MODE_REL);
|
||||
|
||||
/* Don't block the write() function after the first sample to allow the host sending the next samples with no delay */
|
||||
for (i = 0; i < NUM_ACTUATORS; i++)
|
||||
{
|
||||
if ((g_SamplesBuffer[i].actuatorSamples[0].nBufferSize) || (g_SamplesBuffer[i].actuatorSamples[1].nBufferSize))
|
||||
{
|
||||
g_SamplesBuffer[i].nIndexOutputValue = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != VibeOSKernelProcessData(NULL)) return;
|
||||
|
||||
/*
|
||||
** Use interruptible version of down to be safe
|
||||
** (try to not being stuck here if the mutex is not freed for any reason)
|
||||
*/
|
||||
res = down_interruptible(&g_hMutex); /* wait for the mutex to be freed by the timer */
|
||||
if (res != 0)
|
||||
{
|
||||
DbgOut((KERN_INFO "VibeOSKernelLinuxStartTimer: down_interruptible interrupted by a signal.\n"));
|
||||
}
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxStopTimer(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (g_bTimerStarted)
|
||||
{
|
||||
g_bTimerStarted = false;
|
||||
hrtimer_cancel(&g_tspTimer);
|
||||
}
|
||||
|
||||
/* Reset samples buffers */
|
||||
for (i = 0; i < NUM_ACTUATORS; i++)
|
||||
{
|
||||
g_SamplesBuffer[i].nIndexPlayingBuffer = -1;
|
||||
g_SamplesBuffer[i].actuatorSamples[0].nBufferSize = 0;
|
||||
g_SamplesBuffer[i].actuatorSamples[1].nBufferSize = 0;
|
||||
}
|
||||
g_bStopRequested = false;
|
||||
g_bIsPlaying = false;
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxTerminateTimer(void)
|
||||
{
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
if (VibeSemIsLocked(&g_hMutex)) up(&g_hMutex);
|
||||
}
|
|
@ -1,163 +0,0 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** VibeOSKernelLinuxTime_drv2604.c
|
||||
**
|
||||
** Description:
|
||||
** Time helper functions for Linux.
|
||||
**
|
||||
** Portions Copyright (c) 2008-2012 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
|
||||
** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** =========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** Kernel standard software timer is used as an example but another type
|
||||
** of timer (such as HW timer or high-resolution software timer) might be used
|
||||
** to achieve the 5ms required rate.
|
||||
*/
|
||||
|
||||
#if (HZ != 1000)
|
||||
#error The Kernel timer is not configured at 1ms. Please update the code to use HW timer or high-resolution software timer.
|
||||
#endif
|
||||
|
||||
#include <linux/semaphore.h>
|
||||
|
||||
#define WATCHDOG_TIMEOUT 10 /* 10 timer cycles */
|
||||
|
||||
/* Global variables */
|
||||
static bool g_bTimerStarted = false;
|
||||
static struct timer_list g_timerList;
|
||||
static int g_nWatchdogCounter = 0;
|
||||
|
||||
#ifndef NUM_EXTRA_BUFFERS
|
||||
#define NUM_EXTRA_BUFFERS 0
|
||||
#endif
|
||||
struct semaphore g_hSemaphore;
|
||||
|
||||
/* Forward declarations */
|
||||
static void VibeOSKernelLinuxStartTimer(void);
|
||||
static void VibeOSKernelLinuxStopTimer(void);
|
||||
|
||||
static inline int VibeSemIsLocked(struct semaphore *lock)
|
||||
{
|
||||
#if ((LINUX_VERSION_CODE & 0xFFFFFF) < KERNEL_VERSION(2,6,27))
|
||||
return atomic_read(&lock->count) < 1;
|
||||
#else
|
||||
return (lock->count) < 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void VibeOSKernelTimerProc(unsigned long param)
|
||||
{
|
||||
/* Return right away if timer is not supposed to run */
|
||||
if (!g_bTimerStarted) return;
|
||||
|
||||
/* Scheduling next timeout value right away */
|
||||
if (++g_nWatchdogCounter < WATCHDOG_TIMEOUT)
|
||||
mod_timer(&g_timerList, jiffies + g_nTimerPeriodMs);
|
||||
|
||||
if (VibeSemIsLocked(&g_hSemaphore))
|
||||
{
|
||||
up(&g_hSemaphore);
|
||||
}
|
||||
|
||||
if (g_nWatchdogCounter > WATCHDOG_TIMEOUT)
|
||||
{
|
||||
/* Do not call SPI functions in this function as their implementation coud use interrupt */
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
static int VibeOSKernelProcessData(void* data)
|
||||
{
|
||||
SendOutputData();
|
||||
|
||||
/* Reset watchdog counter */
|
||||
g_nWatchdogCounter = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxInitTimer(void)
|
||||
{
|
||||
/* Initialize a 5ms-timer with VibeOSKernelTimerProc as timer callback */
|
||||
init_timer(&g_timerList);
|
||||
g_timerList.function = VibeOSKernelTimerProc;
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxStartTimer(void)
|
||||
{
|
||||
/* Reset watchdog counter */
|
||||
g_nWatchdogCounter = 0;
|
||||
|
||||
if (!g_bTimerStarted)
|
||||
{
|
||||
/* (Re-)Initialize the semaphore used with the timer */
|
||||
sema_init(&g_hSemaphore, NUM_EXTRA_BUFFERS);
|
||||
|
||||
g_bTimerStarted = true;
|
||||
|
||||
/* Start the timer */
|
||||
g_timerList.expires = jiffies + g_nTimerPeriodMs;
|
||||
add_timer(&g_timerList);
|
||||
}
|
||||
else
|
||||
{
|
||||
int res;
|
||||
|
||||
/*
|
||||
** Use interruptible version of down to be safe
|
||||
** (try to not being stuck here if the semaphore is not freed for any reason)
|
||||
*/
|
||||
res = down_interruptible(&g_hSemaphore); /* wait for the semaphore to be freed by the timer */
|
||||
if (res != 0)
|
||||
{
|
||||
DbgOut((DBL_INFO, "VibeOSKernelLinuxStartTimer: down_interruptible interrupted by a signal.\n"));
|
||||
}
|
||||
}
|
||||
|
||||
VibeOSKernelProcessData(NULL);
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxStopTimer(void)
|
||||
{
|
||||
/*
|
||||
** Stop the timer.
|
||||
** The timer is not restarted when the outputdata buffer is empty and it is
|
||||
** automatically removed from the timer list when it expires so there is no need to
|
||||
** call del_timer or del_timer_sync in this function. We just mark it as stopped.
|
||||
*/
|
||||
if (g_bTimerStarted)
|
||||
{
|
||||
g_bTimerStarted = false;
|
||||
}
|
||||
|
||||
/* Reset samples buffers */
|
||||
ResetOutputData();
|
||||
|
||||
g_bIsPlaying = false;
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxTerminateTimer(void)
|
||||
{
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
del_timer_sync(&g_timerList);
|
||||
|
||||
if (VibeSemIsLocked(&g_hSemaphore)) up(&g_hSemaphore);
|
||||
}
|
|
@ -1,272 +0,0 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** VibeOSKernelLinuxTime.c
|
||||
**
|
||||
** Description:
|
||||
** High Resolution Time helper functions for Linux.
|
||||
**
|
||||
** Portions Copyright (c) 2008-2010 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the Immersion Open Source License ?
|
||||
** January 2008 (the 'License'). You may not use this file except in
|
||||
** compliance with the License. Please obtain a copy of the License by
|
||||
** opening the file named "Immersion Open Source License ?January 2008.txt"
|
||||
** that resides in the same folder as the source file tspdrv.c in your
|
||||
** development package. Alternately, a copy of the Immersion Open Source
|
||||
** License ?January 2008 can be obtained from Immersion Corporation by
|
||||
** requesting one via an email addressed to TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
|
||||
** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** ========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** Kernel high-resolution software timer is used as an example but another type
|
||||
** of timer (such as HW timer or standard software timer) might be used to achieve
|
||||
** the 5ms required rate.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_HIGH_RES_TIMERS
|
||||
#warning "The Kernel does not have high resolution timers enabled. Either provide a non hr-timer implementation of VibeOSKernelLinuxTime.c or re-compile your kernel with CONFIG_HIGH_RES_TIMERS=y"
|
||||
#endif
|
||||
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#define WATCHDOG_TIMEOUT 10 /* 10 timer cycles = 50ms */
|
||||
|
||||
/* Global variables */
|
||||
static bool g_bTimerThreadStarted = false;
|
||||
static bool g_bTimerStarted = false;
|
||||
static struct hrtimer g_tspTimer;
|
||||
static ktime_t g_ktFiveMs;
|
||||
struct task_struct *g_pTspThread;
|
||||
static int g_nWatchdogCounter = 0;
|
||||
|
||||
DECLARE_COMPLETION(g_tspCompletion);
|
||||
//DECLARE_MUTEX(g_hMutex);
|
||||
struct semaphore g_hMutex = __SEMAPHORE_INITIALIZER(g_hMutex, 1);
|
||||
|
||||
/* Forward declarations */
|
||||
static void VibeOSKernelLinuxStartTimer(void);
|
||||
static void VibeOSKernelLinuxStopTimer(void);
|
||||
|
||||
/**
|
||||
* VibeSemIsLocked - is the semaphore locked
|
||||
* @lock: the semaphore to be queried
|
||||
*
|
||||
* Returns 1 if the semaphore is locked, 0 if unlocked.
|
||||
*/
|
||||
static inline int VibeSemIsLocked(struct semaphore *lock)
|
||||
{
|
||||
#if ((LINUX_VERSION_CODE & 0xFFFFFF) < KERNEL_VERSION(2,6,27))
|
||||
return atomic_read(&lock->count) != 1;
|
||||
#else
|
||||
return (lock->count) != 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static enum hrtimer_restart tsp_timer_interrupt(struct hrtimer *timer)
|
||||
{
|
||||
/* Scheduling next timeout value right away */
|
||||
hrtimer_forward_now(timer, g_ktFiveMs);
|
||||
|
||||
if(g_bTimerStarted)
|
||||
{
|
||||
/* Notifying the handler of this timer of the tick */
|
||||
/* This allows us to perform operations that are usually */
|
||||
/* not allowed inside interrupt context, such as locking */
|
||||
/* a mutex, etc. */
|
||||
complete(&g_tspCompletion);
|
||||
}
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
|
||||
static int VibeOSKernelTimerProc(void* data)
|
||||
{
|
||||
int nActuatorNotPlaying;
|
||||
int i;
|
||||
int bReachEndBuffer = 0;
|
||||
while(!kthread_should_stop())
|
||||
{
|
||||
if(g_bTimerThreadStarted)
|
||||
{
|
||||
/* Block until we get woken up by timer tick */
|
||||
/* . only do this if we're not exiting entirely */
|
||||
wait_for_completion_interruptible(&g_tspCompletion);
|
||||
|
||||
/* Reinitialized completion so it isn't free by default */
|
||||
init_completion(&g_tspCompletion);
|
||||
}
|
||||
|
||||
nActuatorNotPlaying = 0;
|
||||
|
||||
/* Return right away if timer is not supposed to run */
|
||||
if (g_bTimerStarted)
|
||||
{
|
||||
for (i = 0; i < NUM_ACTUATORS; i++)
|
||||
{
|
||||
actuator_samples_buffer *pCurrentActuatorSample = &(g_SamplesBuffer[i]);
|
||||
|
||||
if (-1 == pCurrentActuatorSample->nIndexPlayingBuffer)
|
||||
{
|
||||
nActuatorNotPlaying++;
|
||||
if ((NUM_ACTUATORS == nActuatorNotPlaying) && ((++g_nWatchdogCounter) > WATCHDOG_TIMEOUT))
|
||||
{
|
||||
/* Nothing to play for all actuators, turn off the timer when we reach the watchdog tick count limit */
|
||||
ImmVibeSPI_ForceOut_Set(i, 0);
|
||||
ImmVibeSPI_ForceOut_AmpDisable(i);
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
/* Reset watchdog counter */
|
||||
g_nWatchdogCounter = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Play the current buffer */
|
||||
ImmVibeSPI_ForceOut_Set(i, pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].dataBuffer[(int)(pCurrentActuatorSample->nIndexOutputValue++)]);
|
||||
if (pCurrentActuatorSample->nIndexOutputValue >= pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize)
|
||||
{
|
||||
/* We were playing in the last tick */
|
||||
/* Reach the end of the current buffer */
|
||||
pCurrentActuatorSample->actuatorSamples[(int)pCurrentActuatorSample->nIndexPlayingBuffer].nBufferSize = 0;
|
||||
bReachEndBuffer = 1;
|
||||
|
||||
/* Check stop request and empty buffer */
|
||||
if ((g_bStopRequested) || (0 == (pCurrentActuatorSample->actuatorSamples[(int)((pCurrentActuatorSample->nIndexPlayingBuffer) ^ 1)].nBufferSize)))
|
||||
{
|
||||
pCurrentActuatorSample->nIndexPlayingBuffer = -1;
|
||||
if(g_bStopRequested)
|
||||
{
|
||||
/* g_bStopReqested is set, so turn off all actuators */
|
||||
ImmVibeSPI_ForceOut_Set(i, 0);
|
||||
ImmVibeSPI_ForceOut_AmpDisable(i);
|
||||
/* If it's the last actuator, stop the timer */
|
||||
if (i == (NUM_ACTUATORS-1))
|
||||
{
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
|
||||
/* Reset watchdog counter */
|
||||
g_nWatchdogCounter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* The other buffer has data in it */
|
||||
{
|
||||
/* Switch buffer */
|
||||
(pCurrentActuatorSample->nIndexPlayingBuffer) ^= 1;
|
||||
pCurrentActuatorSample->nIndexOutputValue = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Release the mutex if locked */
|
||||
if (bReachEndBuffer && VibeSemIsLocked(&g_hMutex))
|
||||
{
|
||||
up(&g_hMutex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxInitTimer(void)
|
||||
{
|
||||
/* Get a 5,000,000ns = 5ms time value */
|
||||
g_ktFiveMs = ktime_set(0, 5000000);
|
||||
|
||||
/* Start the companion thread. It's controlled by the timer, so when that is stopped, we're OK. */
|
||||
g_bTimerThreadStarted = true;
|
||||
g_pTspThread = kthread_run(VibeOSKernelTimerProc, NULL, "TouchSense Player Thread");
|
||||
|
||||
hrtimer_init(&g_tspTimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
|
||||
/* Initialize a 5ms-timer with tsp_timer_interrupt as timer callback (interrupt driven)*/
|
||||
g_tspTimer.function = tsp_timer_interrupt;
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxStartTimer(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Reset watchdog counter */
|
||||
g_nWatchdogCounter = 0;
|
||||
|
||||
if (!g_bTimerStarted)
|
||||
{
|
||||
if (!VibeSemIsLocked(&g_hMutex))
|
||||
{
|
||||
if(down_interruptible(&g_hMutex)) /* start locked */
|
||||
{
|
||||
//printk("down_interruptible failed");
|
||||
}
|
||||
}
|
||||
|
||||
g_bTimerStarted = true;
|
||||
|
||||
/* Start the timer */
|
||||
hrtimer_start(&g_tspTimer, g_ktFiveMs, HRTIMER_MODE_REL);
|
||||
/* Don't block the write() function after the first sample to allow the host sending the next samples with no delay */
|
||||
for (i = 0; i < NUM_ACTUATORS; i++)
|
||||
{
|
||||
if ((g_SamplesBuffer[i].actuatorSamples[0].nBufferSize) || (g_SamplesBuffer[i].actuatorSamples[1].nBufferSize))
|
||||
{
|
||||
g_SamplesBuffer[i].nIndexOutputValue = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Use interruptible version of down to be safe
|
||||
** (try to not being stuck here if the mutex is not freed for any reason)
|
||||
*/
|
||||
if(down_interruptible(&g_hMutex)) /* wait for the mutex to be freed by the timer */
|
||||
{
|
||||
//printk("down_interruptible failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxStopTimer(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (g_bTimerStarted)
|
||||
{
|
||||
g_bTimerStarted = false;
|
||||
hrtimer_cancel(&g_tspTimer);
|
||||
}
|
||||
|
||||
/* Reset samples buffers */
|
||||
for (i = 0; i < NUM_ACTUATORS; i++)
|
||||
{
|
||||
g_SamplesBuffer[i].nIndexPlayingBuffer = -1;
|
||||
g_SamplesBuffer[i].actuatorSamples[0].nBufferSize = 0;
|
||||
g_SamplesBuffer[i].actuatorSamples[1].nBufferSize = 0;
|
||||
}
|
||||
g_bStopRequested = false;
|
||||
g_bIsPlaying = false;
|
||||
}
|
||||
|
||||
static void VibeOSKernelLinuxTerminateTimer(void)
|
||||
{
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
g_bTimerThreadStarted = false;
|
||||
complete_all(&g_tspCompletion);
|
||||
kthread_stop(g_pTspThread);
|
||||
if (VibeSemIsLocked(&g_hMutex)) up(&g_hMutex);
|
||||
}
|
|
@ -1,474 +0,0 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** ImmVibeSPI.c
|
||||
**
|
||||
** Description:
|
||||
** Device-dependent functions called by Immersion TSP API
|
||||
** to control PWM duty cycle, amp enable/disable, save IVT file, etc...
|
||||
**
|
||||
** Portions Copyright (c) 2008-2010 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
|
||||
** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** =========================================================================
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include "tspdrv.h"
|
||||
|
||||
/*
|
||||
** This SPI supports only one actuator.
|
||||
*/
|
||||
#define NUM_ACTUATORS 1
|
||||
#define PWM_DUTY_MAX 213 /* 13MHz / (579 + 1) = 22.4kHz */
|
||||
#define PWM_DEVICE 1
|
||||
|
||||
static bool g_bampenabled;
|
||||
|
||||
struct pm_gpio vib_pwm = {
|
||||
.direction = PM_GPIO_DIR_OUT,
|
||||
.output_buffer = 0,
|
||||
.output_value = 0,
|
||||
.pull = PM_GPIO_PULL_NO,
|
||||
.vin_sel = 0,
|
||||
.out_strength = PM_GPIO_STRENGTH_HIGH,
|
||||
.function = PM_GPIO_FUNC_1,
|
||||
.inv_int_pol = 0,
|
||||
};
|
||||
|
||||
int32_t vibe_set_pwm_freq(int nForce)
|
||||
{
|
||||
/* Put the MND counter in reset mode for programming */
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_SEL_VAL_BMSK,
|
||||
0 << HWIO_GP_SRC_SEL_VAL_SHFT); //SRC_SEL = 000(cxo)
|
||||
#if defined(CONFIG_SEC_BERLUTI_PROJECT) || defined(CONFIG_MACH_S3VE3G_EUR)
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_DIV_VAL_BMSK,
|
||||
23 << HWIO_GP_SRC_DIV_VAL_SHFT); //SRC_DIV = 10111 (Div 12)
|
||||
#else
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_DIV_VAL_BMSK,
|
||||
31 << HWIO_GP_SRC_DIV_VAL_SHFT); //SRC_DIV = 11111 (Div 16)
|
||||
#endif
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_MODE_VAL_BMSK,
|
||||
2 << HWIO_GP_MODE_VAL_SHFT); //Mode Select 10
|
||||
//M value
|
||||
HWIO_OUTM(GP_M_REG, HWIO_GP_MD_REG_M_VAL_BMSK,
|
||||
g_nlra_gp_clk_m << HWIO_GP_MD_REG_M_VAL_SHFT);
|
||||
|
||||
#if defined(CONFIG_MACH_LT03EUR) || defined(CONFIG_MACH_LT03SKT)\
|
||||
|| defined(CONFIG_MACH_LT03KTT) || defined(CONFIG_MACH_LT03LGT) || defined(CONFIG_MACH_PICASSO_LTE)
|
||||
|
||||
if (nForce > 0){
|
||||
g_nforce_32 = g_nlra_gp_clk_n - (((nForce * g_nlra_gp_clk_pwm_mul) >> 8));
|
||||
if(g_nforce_32 < motor_min_strength)
|
||||
g_nforce_32 = motor_min_strength;
|
||||
else
|
||||
g_nforce_32 = (g_nforce_32 - motor_min_strength) \
|
||||
* (g_nlra_gp_clk_n * motor_strength / 100 - motor_min_strength) \
|
||||
/ (g_nlra_gp_clk_n - motor_min_strength) + motor_min_strength;
|
||||
}
|
||||
else{
|
||||
g_nforce_32 = ((nForce * g_nlra_gp_clk_pwm_mul) >> 8) + g_nlra_gp_clk_d;
|
||||
if(g_nlra_gp_clk_n - g_nforce_32 > g_nlra_gp_clk_n * motor_strength /100)
|
||||
g_nforce_32 = g_nlra_gp_clk_n - g_nlra_gp_clk_n * motor_strength /100;
|
||||
}
|
||||
#else
|
||||
if (nForce > 0){
|
||||
g_nforce_32 = g_nlra_gp_clk_n - (((nForce * g_nlra_gp_clk_pwm_mul) >> 8));
|
||||
g_nforce_32 = g_nforce_32 * motor_strength /100;
|
||||
if(g_nforce_32 < motor_min_strength)
|
||||
g_nforce_32 = motor_min_strength;
|
||||
}
|
||||
else{
|
||||
g_nforce_32 = ((nForce * g_nlra_gp_clk_pwm_mul) >> 8) + g_nlra_gp_clk_d;
|
||||
if(g_nlra_gp_clk_n - g_nforce_32 > g_nlra_gp_clk_n * motor_strength /100)
|
||||
g_nforce_32 = g_nlra_gp_clk_n - g_nlra_gp_clk_n * motor_strength /100;
|
||||
}
|
||||
#endif
|
||||
// D value
|
||||
HWIO_OUTM(GP_D_REG, HWIO_GP_MD_REG_D_VAL_BMSK,
|
||||
(~((int16_t)g_nforce_32 << 1)) << HWIO_GP_MD_REG_D_VAL_SHFT);
|
||||
|
||||
//N value
|
||||
HWIO_OUTM(GP_NS_REG, HWIO_GP_NS_REG_GP_N_VAL_BMSK,
|
||||
~(g_nlra_gp_clk_n - g_nlra_gp_clk_m) << 0);
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t vibe_pwm_onoff(u8 onoff)
|
||||
{
|
||||
if (onoff) {
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_UPDATE_VAL_BMSK,
|
||||
1 << HWIO_UPDATE_VAL_SHFT);//UPDATE ACTIVE
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_ROOT_EN_VAL_BMSK,
|
||||
1 << HWIO_ROOT_EN_VAL_SHFT);//ROOT_EN
|
||||
HWIO_OUTM(CAMSS_GP1_CBCR, HWIO_CLK_ENABLE_VAL_BMSK,
|
||||
1 << HWIO_CLK_ENABLE_VAL_SHFT); //CLK_ENABLE
|
||||
} else {
|
||||
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_UPDATE_VAL_BMSK,
|
||||
0 << HWIO_UPDATE_VAL_SHFT);
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_ROOT_EN_VAL_BMSK,
|
||||
0 << HWIO_ROOT_EN_VAL_SHFT);
|
||||
HWIO_OUTM(CAMSS_GP1_CBCR, HWIO_CLK_ENABLE_VAL_BMSK,
|
||||
0 << HWIO_CLK_ENABLE_VAL_SHFT);
|
||||
}
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Called to disable amp (disable output force)
|
||||
*/
|
||||
static int32_t ImmVibeSPI_ForceOut_AmpDisable(u_int8_t nActuatorIndex)
|
||||
{
|
||||
|
||||
if (g_bampenabled) {
|
||||
g_bampenabled = false;
|
||||
if (vibrator_drvdata.power_onoff) {
|
||||
if (!vibrator_drvdata.changed_chip)
|
||||
vibrator_drvdata.power_onoff(0);
|
||||
}
|
||||
if (vibrator_drvdata.vib_model == HAPTIC_PWM) {
|
||||
if(vibrator_drvdata.is_pmic_vib_pwm){ //PMIC PWM
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_OFF);
|
||||
if(vibrator_drvdata.pwm_dev != NULL) //Disable the PWM device.
|
||||
pwm_disable(vibrator_drvdata.pwm_dev);
|
||||
} else{ //AP PWM
|
||||
#if defined(CONFIG_MACH_S3VE3G_EUR) || defined(CONFIG_MACH_VICTOR3GDSDTV_LTN)
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,\
|
||||
0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, \
|
||||
GPIO_CFG_2MA),GPIO_CFG_ENABLE);
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_OFF);
|
||||
#else
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,\
|
||||
0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, \
|
||||
GPIO_CFG_2MA),GPIO_CFG_ENABLE);
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_OFF);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
printk(KERN_DEBUG "tspdrv: %s\n", __func__);
|
||||
#if defined(CONFIG_MOTOR_DRV_MAX77803)
|
||||
max77803_vibtonz_en(0);
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77804K)
|
||||
if (vibrator_drvdata.changed_chip) {
|
||||
gpio_direction_output(vibrator_drvdata.changed_en_gpio, VIBRATION_OFF);
|
||||
gpio_set_value(vibrator_drvdata.changed_en_gpio,VIBRATION_OFF);
|
||||
} else
|
||||
max77804k_vibtonz_en(0);
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77828)
|
||||
max77828_vibtonz_en(0);
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77888)
|
||||
max77888_gpio_en(0);
|
||||
max77888_vibtonz_en(0);
|
||||
#elif defined(CONFIG_MOTOR_DRV_DRV2603)
|
||||
drv2603_gpio_en(0);
|
||||
#elif defined(CONFIG_MOTOR_ISA1000)
|
||||
gpio_direction_output(vibrator_drvdata.vib_en_gpio,VIBRATION_OFF);
|
||||
gpio_set_value(vibrator_drvdata.vib_en_gpio,VIBRATION_OFF);
|
||||
#endif
|
||||
}
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called to enable amp (enable output force)
|
||||
*/
|
||||
static int32_t ImmVibeSPI_ForceOut_AmpEnable(u_int8_t nActuatorIndex)
|
||||
{
|
||||
if (!g_bampenabled) {
|
||||
g_bampenabled = true;
|
||||
if (vibrator_drvdata.power_onoff) {
|
||||
if (!vibrator_drvdata.changed_chip)
|
||||
vibrator_drvdata.power_onoff(1);
|
||||
}
|
||||
if (vibrator_drvdata.vib_model == HAPTIC_PWM) {
|
||||
if(vibrator_drvdata.is_pmic_vib_pwm){ //PMIC PWM
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_ON);
|
||||
} else { //AP PWM
|
||||
#if defined(CONFIG_MACH_HLTEDCM) || defined(CONFIG_MACH_HLTEKDI) || \
|
||||
defined(CONFIG_MACH_JS01LTEDCM) || defined(CONFIG_MACH_JS01LTESBM)
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,\
|
||||
2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, \
|
||||
GPIO_CFG_2MA), GPIO_CFG_ENABLE);
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_ON);
|
||||
#elif defined(CONFIG_SEC_BERLUTI_PROJECT) || defined(CONFIG_MACH_S3VE3G_EUR) || defined(CONFIG_MACH_VICTOR3GDSDTV_LTN) || defined(CONFIG_SEC_HESTIA_PROJECT)
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,\
|
||||
3, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, \
|
||||
GPIO_CFG_2MA), GPIO_CFG_ENABLE);
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_ON);
|
||||
#else
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,\
|
||||
6, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, \
|
||||
GPIO_CFG_2MA), GPIO_CFG_ENABLE);
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_ON);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
printk(KERN_DEBUG "tspdrv: %s\n", __func__);
|
||||
#if defined(CONFIG_MOTOR_DRV_MAX77803)
|
||||
max77803_vibtonz_en(1);
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77804K)
|
||||
if (vibrator_drvdata.changed_chip) {
|
||||
gpio_direction_output(vibrator_drvdata.changed_en_gpio, VIBRATION_ON);
|
||||
gpio_set_value(vibrator_drvdata.changed_en_gpio,VIBRATION_ON);
|
||||
} else
|
||||
max77804k_vibtonz_en(1);
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77828)
|
||||
max77828_vibtonz_en(1);
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77888)
|
||||
max77888_gpio_en(1);
|
||||
max77888_vibtonz_en(1);
|
||||
#elif defined(CONFIG_MOTOR_DRV_DRV2603)
|
||||
drv2603_gpio_en(1);
|
||||
#elif defined(CONFIG_MOTOR_ISA1000)
|
||||
gpio_direction_output(vibrator_drvdata.vib_en_gpio,VIBRATION_ON);
|
||||
gpio_set_value(vibrator_drvdata.vib_en_gpio,VIBRATION_ON);
|
||||
#endif
|
||||
}
|
||||
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called at initialization time to set PWM freq,
|
||||
** disable amp, etc...
|
||||
*/
|
||||
static int32_t ImmVibeSPI_ForceOut_Initialize(void)
|
||||
{
|
||||
int ret;
|
||||
DbgOut((KERN_DEBUG "ImmVibeSPI_ForceOut_Initialize.\n"));
|
||||
/* to force ImmVibeSPI_ForceOut_AmpDisable disabling the amp */
|
||||
g_bampenabled = true;
|
||||
|
||||
/*
|
||||
** Disable amp.
|
||||
** If multiple actuators are supported, please make sure to call
|
||||
** ImmVibeSPI_ForceOut_AmpDisable for each actuator
|
||||
** (provide the actuator index as input argument).
|
||||
*/
|
||||
|
||||
/* set gpio config */
|
||||
if (vibrator_drvdata.vib_model == HAPTIC_PWM) {
|
||||
if (vibrator_drvdata.is_pmic_vib_pwm) { //PMIC PWM
|
||||
ret = gpio_request(vibrator_drvdata.vib_pwm_gpio, \
|
||||
"vib pwm");
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR"vib pwm gpio_request is failed\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = pm8xxx_gpio_config(vibrator_drvdata.vib_pwm_gpio,\
|
||||
&vib_pwm);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "failed to configure vib pwm pmic gpio\n");
|
||||
goto err2;
|
||||
}
|
||||
} else { //AP PWM
|
||||
#if defined(CONFIG_MACH_S3VE3G_EUR)
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,
|
||||
0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),
|
||||
GPIO_CFG_ENABLE);
|
||||
#else
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,
|
||||
0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
|
||||
GPIO_CFG_ENABLE);
|
||||
#endif
|
||||
}
|
||||
#if defined(CONFIG_MOTOR_ISA1000)
|
||||
if (!vibrator_drvdata.is_pmic_vib_en) {
|
||||
ret = gpio_request(vibrator_drvdata.vib_en_gpio,"vib enable");
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "vib enable gpio_request is failed\n");
|
||||
goto err2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MOTOR_DRV_DRV2603)
|
||||
if (drv2603_gpio_init())
|
||||
goto err2;
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77888)
|
||||
if(max77888_gpio_init())
|
||||
goto err2;
|
||||
#endif
|
||||
}
|
||||
ImmVibeSPI_ForceOut_AmpDisable(0);
|
||||
return VIBE_S_SUCCESS;
|
||||
|
||||
err2:
|
||||
return VIBE_E_FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called at termination time to set PWM freq, disable amp, etc...
|
||||
*/
|
||||
static int32_t ImmVibeSPI_ForceOut_Terminate(void)
|
||||
{
|
||||
DbgOut((KERN_DEBUG "ImmVibeSPI_ForceOut_Terminate.\n"));
|
||||
|
||||
/*
|
||||
** Disable amp.
|
||||
** If multiple actuators are supported, please make sure to call
|
||||
** ImmVibeSPI_ForceOut_AmpDisable for each actuator
|
||||
** (provide the actuator index as input argument).
|
||||
*/
|
||||
ImmVibeSPI_ForceOut_AmpDisable(0);
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int vib_config_pwm_device(void)
|
||||
{
|
||||
int ret = 0;
|
||||
if(vibrator_drvdata.pwm_dev == NULL){
|
||||
//u32 pwm_period_us, duty_us;
|
||||
#if defined(CONFIG_MACH_HLTEDCM) || defined(CONFIG_MACH_HLTEKDI) || \
|
||||
defined(CONFIG_MACH_JS01LTEDCM) || defined(CONFIG_MACH_JS01LTESBM)
|
||||
vibrator_drvdata.pwm_dev = pwm_request(0,"lpg_3"); // 0 index for LPG3 channel.
|
||||
#else
|
||||
vibrator_drvdata.pwm_dev = pwm_request(0,"lpg_1"); // 0 index for LPG1 channel.
|
||||
#endif
|
||||
|
||||
if (IS_ERR_OR_NULL(vibrator_drvdata.pwm_dev)) {
|
||||
pr_err("could not acquire PWM Channel 0, "
|
||||
"error %ld\n",PTR_ERR(vibrator_drvdata.pwm_dev));
|
||||
vibrator_drvdata.pwm_dev = NULL;
|
||||
return -ENODEV;
|
||||
}
|
||||
//pwm_period_us = 19; // 2000000;
|
||||
//duty_us = 18; //1000000; (90% Duty Cycle)
|
||||
|
||||
ret = pwm_config(vibrator_drvdata.pwm_dev,
|
||||
vibrator_drvdata.duty_us,
|
||||
vibrator_drvdata.pwm_period_us);
|
||||
if (ret) {
|
||||
pr_err("pwm_config in vibrator enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = pwm_enable(vibrator_drvdata.pwm_dev);
|
||||
if (ret < 0) {
|
||||
pr_err("pwm_enable in vibrator failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = pwm_enable(vibrator_drvdata.pwm_dev);
|
||||
if (ret < 0) {
|
||||
pr_err("pwm_enable in vibrator failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called by the real-time loop to set PWM duty cycle
|
||||
*/
|
||||
#ifdef CONFIG_TACTILE_ASSIST
|
||||
static bool g_bOutputDataBufferEmpty = 1;
|
||||
#endif
|
||||
static int32_t ImmVibeSPI_ForceOut_SetSamples(u_int8_t nActuatorIndex,
|
||||
u_int16_t nOutputSignalBitDepth,
|
||||
u_int16_t nBufferSizeInBytes,
|
||||
int8_t *pForceOutputBuffer)
|
||||
{
|
||||
int8_t nforce;
|
||||
static int8_t pre_nforce;
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_TACTILE_ASSIST
|
||||
if (g_bOutputDataBufferEmpty) {
|
||||
nActuatorIndex = 0;
|
||||
nOutputSignalBitDepth = 8;
|
||||
nBufferSizeInBytes = 1;
|
||||
pForceOutputBuffer[0] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (nOutputSignalBitDepth) {
|
||||
case 8:
|
||||
/* pForceOutputBuffer is expected to contain 1 byte */
|
||||
if (nBufferSizeInBytes != 1) {
|
||||
DbgOut(KERN_ERR
|
||||
"[ImmVibeSPI] ImmVibeSPI_ForceOut_SetSamples nBufferSizeInBytes = %d\n",
|
||||
nBufferSizeInBytes);
|
||||
return VIBE_E_FAIL;
|
||||
}
|
||||
nforce = pForceOutputBuffer[0];
|
||||
break;
|
||||
case 16:
|
||||
/* pForceOutputBuffer is expected to contain 2 byte */
|
||||
if (nBufferSizeInBytes != 2)
|
||||
return VIBE_E_FAIL;
|
||||
|
||||
/* Map 16-bit value to 8-bit */
|
||||
nforce = ((int16_t *)pForceOutputBuffer)[0] >> 8;
|
||||
break;
|
||||
default:
|
||||
/* Unexpected bit depth */
|
||||
return VIBE_E_FAIL;
|
||||
}
|
||||
|
||||
if (nforce == 0) {
|
||||
/* Set 50% duty cycle or disable amp */
|
||||
ImmVibeSPI_ForceOut_AmpDisable(0);
|
||||
if (!vibrator_drvdata.is_pmic_vib_pwm){
|
||||
//AP PWM
|
||||
vibe_pwm_onoff(0);
|
||||
}
|
||||
nforce = 0;
|
||||
pre_nforce = 0;
|
||||
} else {
|
||||
if (nforce > 0)
|
||||
nforce = 127 - nforce;
|
||||
/* Map force from [-127, 127] to [0, PWM_DUTY_MAX] */
|
||||
/* printk(KERN_DEBUG "[tspdrv]nForce===%d\n", nforce); */
|
||||
if (pre_nforce != nforce) {
|
||||
if (vibrator_drvdata.is_pmic_vib_pwm){
|
||||
//PMIC PWM
|
||||
ret = vib_config_pwm_device();
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
}else {
|
||||
//AP PWM
|
||||
vibe_set_pwm_freq(nforce);
|
||||
vibe_pwm_onoff(1);
|
||||
}
|
||||
ImmVibeSPI_ForceOut_AmpEnable(0);
|
||||
pre_nforce = nforce;
|
||||
}
|
||||
}
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called to get the device name (device name must be returned as ANSI char)
|
||||
*/
|
||||
static int32_t ImmVibeSPI_Device_GetName(
|
||||
u_int8_t nActuatorIndex, char *szDevName, int nSize)
|
||||
{
|
||||
return VIBE_S_SUCCESS;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
** DRV2604 register settings for initialization of SEMCO0934 LRA
|
||||
**
|
||||
** $Revision: 35184 $
|
||||
*/
|
||||
MODE_REG, MODE_INTERNAL_TRIGGER,
|
||||
RATED_VOLTAGE_REG, LRA_RATED_VOLTAGE,
|
||||
OVERDRIVE_CLAMP_VOLTAGE_REG, LRA_OVERDRIVE_CLAMP_VOLTAGE,
|
||||
AUTO_CALI_RESULT_REG, DEFAULT_LRA_AUTOCAL_COMPENSATION,
|
||||
AUTO_CALI_BACK_EMF_RESULT_REG, 0xFF,
|
||||
FEEDBACK_CONTROL_REG, FEEDBACK_CONTROL_MODE_LRA | FB_BRAKE_FACTOR_4X | LOOP_RESPONSE_MEDIUM | FEEDBACK_CONTROL_BEMF_LRA_GAIN3,
|
||||
Control1_REG, STARTUP_BOOST_ENABLED | LRA_DRIVE_TIME,
|
||||
Control2_REG, BIDIRECT_INPUT | SOFT_BRAKE | AUTO_RES_GAIN_MEDIUM | BLANKING_TIME_MEDIUM | IDISS_TIME_MEDIUM,
|
||||
Control3_REG, NG_Thresh_2,
|
||||
AUTOCAL_MEM_INTERFACE_REG, AUTOCAL_TIME_1000MS,
|
|
@ -0,0 +1,451 @@
|
|||
/* drivers/motor/isa1200_vibrator.c
|
||||
*
|
||||
* Copyright (C) 2011 Samsung Electronics Co. Ltd. All Rights Reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/vibrator.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/earlysuspend.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/regulator/krait-regulator.h>
|
||||
|
||||
#include "../staging/android/timed_output.h"
|
||||
#include "isa1200_vibrator.h"
|
||||
|
||||
|
||||
static struct hrtimer timer;
|
||||
|
||||
static int max_timeout = 10000;
|
||||
static int vibrator_value = 0;
|
||||
static int vibrator_work;
|
||||
static int isa1200_enabled;
|
||||
|
||||
|
||||
static void _set_vibrator_work(struct work_struct *unused);
|
||||
|
||||
static DECLARE_WORK(vib_work, _set_vibrator_work);
|
||||
struct vibrator_platform_data_isa1200 vibrator_drvdata;
|
||||
|
||||
#if defined (CONFIG_MACH_MATISSE3G_OPEN) || defined (CONFIG_SEC_MATISSELTE_COMMON) || defined (CONFIG_MACH_T10_3G_OPEN)
|
||||
static void haptic_power_onoff(int onoff)
|
||||
{
|
||||
int ret;
|
||||
static struct regulator *reg_l6;
|
||||
|
||||
if (!reg_l6) {
|
||||
reg_l6 = regulator_get(&vibrator_drvdata.client->dev,"vddo");
|
||||
if (IS_ERR(reg_l6)) {
|
||||
printk(KERN_ERR"could not get 8226_l6, rc = %ld\n",
|
||||
PTR_ERR(reg_l6));
|
||||
return;
|
||||
}
|
||||
ret = regulator_set_voltage(reg_l6, 1800000, 1800000);
|
||||
}
|
||||
|
||||
if (onoff) {
|
||||
ret = regulator_enable(reg_l6);
|
||||
if (ret) {
|
||||
printk(KERN_ERR"enable l6 failed, rc=%d\n", ret);
|
||||
return;
|
||||
}
|
||||
printk(KERN_DEBUG"haptic power_on is finished.\n");
|
||||
} else {
|
||||
if (regulator_is_enabled(reg_l6)) {
|
||||
ret = regulator_disable(reg_l6);
|
||||
if (ret) {
|
||||
printk(KERN_ERR"disable l6 failed, rc=%d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
printk(KERN_DEBUG"haptic power_off is finished.\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void vibe_set_pwm_freq(int intensity)
|
||||
{
|
||||
int32_t calc_d;
|
||||
|
||||
/* Put the MND counter in reset mode for programming */
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_SEL_VAL_BMSK,0 << HWIO_GP_SRC_SEL_VAL_SHFT); //SRC_SEL = 000(cxo)
|
||||
#if defined (CONFIG_MACH_MATISSELTE_ATT)
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_DIV_VAL_BMSK,23 << HWIO_GP_SRC_DIV_VAL_SHFT); //SRC_DIV = 11111 (Div 12)
|
||||
#else
|
||||
#if defined (CONFIG_MACH_MATISSE3G_OPEN) || defined (CONFIG_SEC_MATISSELTE_COMMON)
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_DIV_VAL_BMSK,29 << HWIO_GP_SRC_DIV_VAL_SHFT); //SRC_DIV = 11111 (Div 16)
|
||||
#else
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_DIV_VAL_BMSK,31 << HWIO_GP_SRC_DIV_VAL_SHFT); //SRC_DIV = 11111 (Div 16)
|
||||
#endif
|
||||
#endif//CONFIG_MACH_MATISSELTE_ATT
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_MODE_VAL_BMSK,2 << HWIO_GP_MODE_VAL_SHFT); //Mode Select 10
|
||||
//M value
|
||||
HWIO_OUTM(GP_M_REG, HWIO_GP_MD_REG_M_VAL_BMSK,g_nlra_gp_clk_m << HWIO_GP_MD_REG_M_VAL_SHFT);
|
||||
if (intensity > 0){
|
||||
calc_d = g_nlra_gp_clk_n - (((intensity * g_nlra_gp_clk_pwm_mul) >> 8));
|
||||
calc_d = calc_d * motor_strength /100;
|
||||
if(calc_d < motor_min_strength)
|
||||
calc_d = motor_min_strength;
|
||||
}
|
||||
else {
|
||||
calc_d = ((intensity * g_nlra_gp_clk_pwm_mul) >> 8) + g_nlra_gp_clk_d;
|
||||
if(g_nlra_gp_clk_n - calc_d > g_nlra_gp_clk_n * motor_strength /100)
|
||||
calc_d = g_nlra_gp_clk_n - g_nlra_gp_clk_n * motor_strength /100;
|
||||
}
|
||||
// D value
|
||||
HWIO_OUTM(GP_D_REG, HWIO_GP_MD_REG_D_VAL_BMSK,(~((int16_t)calc_d << 1)) << HWIO_GP_MD_REG_D_VAL_SHFT);
|
||||
//N value
|
||||
HWIO_OUTM(GP_NS_REG, HWIO_GP_NS_REG_GP_N_VAL_BMSK,~(g_nlra_gp_clk_n - g_nlra_gp_clk_m) << 0);
|
||||
}
|
||||
|
||||
|
||||
static void vibe_pwm_onoff(u8 onoff)
|
||||
{
|
||||
if (onoff) {
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_UPDATE_VAL_BMSK,
|
||||
1 << HWIO_UPDATE_VAL_SHFT);//UPDATE ACTIVE
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_ROOT_EN_VAL_BMSK,
|
||||
1 << HWIO_ROOT_EN_VAL_SHFT);//ROOT_EN
|
||||
HWIO_OUTM(CAMSS_GP1_CBCR, HWIO_CLK_ENABLE_VAL_BMSK,
|
||||
1 << HWIO_CLK_ENABLE_VAL_SHFT); //CLK_ENABLE
|
||||
} else {
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_UPDATE_VAL_BMSK,
|
||||
0 << HWIO_UPDATE_VAL_SHFT);
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_ROOT_EN_VAL_BMSK,
|
||||
0 << HWIO_ROOT_EN_VAL_SHFT);
|
||||
HWIO_OUTM(CAMSS_GP1_CBCR, HWIO_CLK_ENABLE_VAL_BMSK,
|
||||
0 << HWIO_CLK_ENABLE_VAL_SHFT);
|
||||
}
|
||||
}
|
||||
|
||||
static int vib_isa1200_onoff(u8 onoff)
|
||||
{
|
||||
|
||||
if(onoff) {
|
||||
vibrator_write_register(HCTRL0, 0x88); //HCTRL0
|
||||
vibrator_write_register(HCTRL1, 0x4B);
|
||||
} else {
|
||||
vibrator_write_register(HCTRL0, 0x08);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vibe_set_intensity(int intensity)
|
||||
{
|
||||
if (0 == intensity)
|
||||
vibe_pwm_onoff(0);
|
||||
else {
|
||||
if (MAX_INTENSITY == intensity)
|
||||
intensity = 1;
|
||||
else if (0 != intensity) {
|
||||
int tmp = MAX_INTENSITY - intensity;
|
||||
intensity = (tmp / 79); // 79 := 10000 / 127
|
||||
}
|
||||
|
||||
vibe_set_pwm_freq(intensity);
|
||||
vibe_pwm_onoff(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void isa1200_initialize(void)
|
||||
{
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_clk,0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),GPIO_CFG_ENABLE);
|
||||
msleep(1);
|
||||
vibrator_write_register(HCTRL0, 0x08);
|
||||
vibrator_write_register(HCTRL1, 0x4B);
|
||||
}
|
||||
|
||||
static int set_vibrator(int timeout)
|
||||
{
|
||||
if(timeout) {
|
||||
#if defined (CONFIG_MACH_MATISSE3G_OPEN) || defined (CONFIG_SEC_MATISSELTE_COMMON) || defined (CONFIG_MACH_T10_3G_OPEN)
|
||||
vibrator_drvdata.power_onoff(1);
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_clk, 3, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),GPIO_CFG_ENABLE);
|
||||
#else
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_clk, 6, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),GPIO_CFG_ENABLE);
|
||||
#endif
|
||||
gpio_set_value(vibrator_drvdata.vib_clk, VIBRATION_ON);
|
||||
gpio_set_value(vibrator_drvdata.motor_en, VIBRATION_ON);
|
||||
#if defined (CONFIG_MACH_MATISSE3G_OPEN) || defined (CONFIG_SEC_MATISSELTE_COMMON)
|
||||
msleep(1);
|
||||
#endif
|
||||
vib_isa1200_onoff(1);
|
||||
} else {
|
||||
vib_isa1200_onoff(0);
|
||||
#if defined (CONFIG_MACH_MATISSE3G_OPEN) || defined (CONFIG_SEC_MATISSELTE_COMMON) || defined (CONFIG_MACH_T10_3G_OPEN)
|
||||
vibrator_drvdata.power_onoff(0);
|
||||
#endif
|
||||
gpio_set_value(vibrator_drvdata.motor_en, VIBRATION_OFF);
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_clk, 0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),GPIO_CFG_ENABLE);
|
||||
gpio_set_value(vibrator_drvdata.vib_clk, VIBRATION_OFF);
|
||||
}
|
||||
|
||||
vibrator_value = timeout;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _set_vibrator_work(struct work_struct *unused)
|
||||
{
|
||||
set_vibrator(vibrator_work);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static enum hrtimer_restart vibtation_timer_func(struct hrtimer *timer)
|
||||
{
|
||||
vibrator_work = 0;
|
||||
schedule_work(&vib_work);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static int get_time_for_vibtation(struct timed_output_dev *dev)
|
||||
{
|
||||
int remaining;
|
||||
|
||||
if (hrtimer_active(&timer)) {
|
||||
ktime_t r = hrtimer_get_remaining(&timer);
|
||||
remaining = (int)ktime_to_ms(r);
|
||||
} else
|
||||
remaining = 0;
|
||||
|
||||
if (vibrator_value == -1)
|
||||
remaining = -1;
|
||||
|
||||
return remaining;
|
||||
|
||||
}
|
||||
|
||||
static void enable_vibtation_from_user(struct timed_output_dev *dev,int value)
|
||||
{
|
||||
printk("[VIB] : time = %d msec \n", value);
|
||||
hrtimer_cancel(&timer);
|
||||
/* set_vibrator(value); */
|
||||
vibrator_work = value;
|
||||
schedule_work(&vib_work);
|
||||
|
||||
if (value > 0)
|
||||
{
|
||||
if (value > max_timeout)
|
||||
value = max_timeout;
|
||||
|
||||
hrtimer_start(&timer,ktime_set(value / 1000, (value % 1000) * 1000000),
|
||||
HRTIMER_MODE_REL);
|
||||
vibrator_value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct timed_output_dev timed_output_vt = {
|
||||
.name = "vibrator",
|
||||
.get_time = get_time_for_vibtation,
|
||||
.enable = enable_vibtation_from_user,
|
||||
};
|
||||
|
||||
static ssize_t intensity_store(struct device *dev,
|
||||
struct device_attribute *devattr, const char *buf, size_t count)
|
||||
{
|
||||
int ret = 0, set_intensity = 0;
|
||||
|
||||
ret = kstrtoint(buf, 0, &set_intensity);
|
||||
|
||||
if ((set_intensity < 0) || (set_intensity > MAX_INTENSITY)) {
|
||||
pr_err("[VIB]: %sout of rage\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vibe_set_intensity(set_intensity);
|
||||
vibrator_drvdata.intensity = set_intensity;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t intensity_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
||||
return sprintf(buf, "intensity: %u\n", vibrator_drvdata.intensity);
|
||||
}
|
||||
static DEVICE_ATTR(intensity, 0660, intensity_show, intensity_store);
|
||||
|
||||
static void vibtation_start(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
timer.function = vibtation_timer_func;
|
||||
|
||||
ret = timed_output_dev_register(&timed_output_vt);
|
||||
if(ret)
|
||||
pr_info("[VIB]: timed_output_dev_register is fail\n");
|
||||
|
||||
ret = sysfs_create_file(&timed_output_vt.dev->kobj, &dev_attr_intensity.attr);
|
||||
if (ret < 0) {
|
||||
pr_err("[VIB]: Failed to register sysfs intensity: %d\n", ret);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Module info */
|
||||
|
||||
int vibrator_write_register(u8 addr, u8 w_data)
|
||||
{
|
||||
|
||||
if (i2c_smbus_write_byte_data(vibrator_drvdata.client, addr, w_data) < 0) {
|
||||
pr_err("%s: Failed to write addr=[0x%x], data=[0x%x]\n",
|
||||
__func__, addr, w_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int isa1200_parse_dt(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
vibrator_drvdata.motor_en = of_get_named_gpio(np, "isa1200,motor_en",0);
|
||||
vibrator_drvdata.vib_clk = of_get_named_gpio(np, "isa1200,vib_clk",0);
|
||||
#if defined(CONFIG_MACH_T10_3G_OPEN)
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.motor_en, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),GPIO_CFG_DISABLE);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int __devinit isa1200_vibrator_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
||||
int error; /* initialized below */
|
||||
pr_info("[VIB]: %s\n", __func__);
|
||||
motor_min_strength = g_nlra_gp_clk_n*MOTOR_MIN_STRENGTH/100;
|
||||
|
||||
vibrator_drvdata.client = client;
|
||||
if(!(client->dev.of_node)){
|
||||
pr_info("[VIB]: %s failed, DT is NULL", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
error = isa1200_parse_dt(&client->dev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if( gpio_request(vibrator_drvdata.motor_en, "MOTOR_EN") < 0)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpio_direction_output(vibrator_drvdata.motor_en, 0);
|
||||
gpio_export(vibrator_drvdata.motor_en, 0);
|
||||
|
||||
virt_mmss_gp1_base = ioremap(MSM_MMSS_GP1_BASE,0x28);
|
||||
if (!virt_mmss_gp1_base)
|
||||
panic("[VIB]: Unable to ioremap MSM_MMSS_GP1 memory!");
|
||||
|
||||
#if defined(CONFIG_MACH_MATISSE3G_OPEN) || defined (CONFIG_SEC_MATISSELTE_COMMON) || defined (CONFIG_MACH_T10_3G_OPEN)
|
||||
vibrator_drvdata.power_onoff = haptic_power_onoff;
|
||||
#endif
|
||||
vibrator_drvdata.intensity = MAX_INTENSITY;
|
||||
isa1200_initialize();
|
||||
vibe_set_intensity(vibrator_drvdata.intensity);
|
||||
|
||||
isa1200_enabled = 1;
|
||||
|
||||
vibtation_start();
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int __devexit isa1200_remove(struct i2c_client *client)
|
||||
{
|
||||
pr_info("[VIB]: isa1200_remove_module.\n");
|
||||
iounmap(virt_mmss_gp1_base);
|
||||
gpio_free(vibrator_drvdata.motor_en);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isa1200_vibrator_suspend(struct i2c_client *client,pm_message_t mesg)
|
||||
{
|
||||
|
||||
int ret = 0;
|
||||
|
||||
if(isa1200_enabled){
|
||||
vibrator_write_register(0x30, 0x08);
|
||||
gpio_set_value(vibrator_drvdata.motor_en, VIBRATION_OFF);
|
||||
isa1200_enabled = 0;
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int isa1200_vibrator_resume(struct i2c_client *client)
|
||||
{
|
||||
pr_info("[VIB]:isa1200_vibrator_resume\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id isa1200_vibrator_device_id[] = {
|
||||
{"isa1200_vibrator", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, isa1200_vibrator_device_id);
|
||||
|
||||
static struct of_device_id isa1200_vibrator_table[] = {
|
||||
{ .compatible = "isa1200_vibrator,vibrator",},
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct i2c_driver isa1200_vibrator_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "isa1200_vibrator",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = isa1200_vibrator_table,
|
||||
},
|
||||
.probe = isa1200_vibrator_i2c_probe,
|
||||
.id_table = isa1200_vibrator_device_id,
|
||||
.suspend = isa1200_vibrator_suspend,
|
||||
.resume = isa1200_vibrator_resume,
|
||||
.remove = __devexit_p(isa1200_remove),
|
||||
};
|
||||
|
||||
static int __init isa1200_vibrator_init(void)
|
||||
{
|
||||
return(i2c_add_driver(&isa1200_vibrator_i2c_driver));
|
||||
}
|
||||
|
||||
static void __exit isa1200_vibrator_exit(void)
|
||||
{
|
||||
i2c_del_driver(&isa1200_vibrator_i2c_driver);
|
||||
}
|
||||
|
||||
module_init(isa1200_vibrator_init);
|
||||
module_exit(isa1200_vibrator_exit);
|
||||
|
||||
MODULE_AUTHOR("Samsung Corporation");
|
||||
MODULE_DESCRIPTION("Vibrator driver for the isa1200 ic");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -1,89 +1,27 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** tspdrv_isa1200.h
|
||||
**
|
||||
** Description:
|
||||
** Constants and type definitions for the TouchSense Kernel Module.
|
||||
**
|
||||
** Portions Copyright (c) 2008-2010 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** =========================================================================
|
||||
*/
|
||||
|
||||
#ifndef _TSPDRV_H
|
||||
#define _TSPDRV_H
|
||||
/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only 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.
|
||||
*/
|
||||
#ifndef _ISA1200_VIBRATOR_H
|
||||
#define _ISA1200_VIBRATOR_H
|
||||
|
||||
#include <mach/msm_iomap.h>
|
||||
|
||||
/* Constants */
|
||||
#define MODULE_NAME "tspdrv"
|
||||
#define TSPDRV "/dev/"MODULE_NAME
|
||||
#define TSPDRV_MAGIC_NUMBER 0x494D4D52
|
||||
#if defined(CONFIG_VIBRATOR_UPDATE)
|
||||
#define TSPDRV_IOCTL_GROUP 0x52
|
||||
#define TSPDRV_SET_MAGIC_NUMBER _IO(TSPDRV_IOCTL_GROUP, 2)
|
||||
#endif
|
||||
#define TSPDRV_STOP_KERNEL_TIMER _IO(TSPDRV_MAGIC_NUMBER & 0xFF, 1)
|
||||
/*
|
||||
** Obsolete IOCTL command
|
||||
** #define TSPDRV_IDENTIFY_CALLER _IO(TSPDRV_MAGIC_NUMBER & 0xFF, 2)
|
||||
*/
|
||||
#define TSPDRV_ENABLE_AMP _IO(TSPDRV_MAGIC_NUMBER & 0xFF, 3)
|
||||
#define TSPDRV_DISABLE_AMP _IO(TSPDRV_MAGIC_NUMBER & 0xFF, 4)
|
||||
#define TSPDRV_GET_NUM_ACTUATORS _IO(TSPDRV_MAGIC_NUMBER & 0xFF, 5)
|
||||
#define VIBE_MAX_DEVICE_NAME_LENGTH 64
|
||||
#define SPI_HEADER_SIZE 3 /* DO NOT CHANGE - SPI buffer header size */
|
||||
#define VIBE_OUTPUT_SAMPLE_SIZE 50 /* DO NOT CHANGE - maximum number of samples */
|
||||
|
||||
/* Type definitions */
|
||||
#ifdef __KERNEL__
|
||||
typedef int8_t VibeInt8;
|
||||
typedef u_int8_t VibeUInt8;
|
||||
typedef int16_t VibeInt16;
|
||||
typedef u_int16_t VibeUInt16;
|
||||
typedef int32_t VibeInt32;
|
||||
typedef u_int32_t VibeUInt32;
|
||||
typedef u_int8_t VibeBool;
|
||||
typedef VibeInt32 VibeStatus;
|
||||
|
||||
typedef struct {
|
||||
VibeUInt8 nActuatorIndex; /* 1st byte is actuator index */
|
||||
VibeUInt8 nBitDepth; /* 2nd byte is bit depth */
|
||||
VibeUInt8 nBufferSize; /* 3rd byte is data size */
|
||||
VibeUInt8 dataBuffer[VIBE_OUTPUT_SAMPLE_SIZE];
|
||||
} samples_buffer;
|
||||
|
||||
typedef struct {
|
||||
VibeInt8 nIndexPlayingBuffer;
|
||||
VibeUInt8 nIndexOutputValue;
|
||||
samples_buffer actuatorSamples[2]; /* Use 2 buffers to receive samples from user mode */
|
||||
} actuator_samples_buffer;
|
||||
|
||||
#endif
|
||||
|
||||
/* Error and Return value codes */
|
||||
#define VIBE_S_SUCCESS 0 /* Success */
|
||||
#define VIBE_E_FAIL -4 /* Generic error */
|
||||
|
||||
|
||||
#define VIBRATION_ON 1
|
||||
#define VIBRATION_OFF 0
|
||||
|
||||
#define HCTRL0 (0x30) /* 0x09 */ /* Haptic Motor Driver Control Register Group 0*/
|
||||
#define HCTRL1 (0x31) /* 0x4B */ /* Haptic Motor Driver Control Register Group 1*/
|
||||
|
||||
#define MAX_INTENSITY 10000
|
||||
|
||||
#define GP_CLK_M_DEFAULT 2
|
||||
|
||||
#if defined (CONFIG_MACH_MATISSELTE_ATT)
|
||||
|
@ -106,9 +44,6 @@ typedef struct {
|
|||
* ** Global variables for LRA PWM M,N and D values.
|
||||
* */
|
||||
|
||||
|
||||
int32_t g_nforce_32 = 0;
|
||||
|
||||
#define MOTOR_MIN_STRENGTH 54/*IMMERSION VALUE*/
|
||||
#define MOTOR_STRENGTH 98/*MOTOR_STRENGTH 98 %*/
|
||||
int32_t g_nlra_gp_clk_m = GP_CLK_M_DEFAULT;
|
||||
|
@ -118,36 +53,6 @@ int32_t g_nlra_gp_clk_pwm_mul = IMM_PWM_MULTIPLIER;
|
|||
int32_t motor_strength = MOTOR_STRENGTH;
|
||||
int32_t motor_min_strength;
|
||||
|
||||
|
||||
#if defined(VIBE_RECORD) && defined(VIBE_DEBUG)
|
||||
void _RecorderInit(void);
|
||||
void _RecorderTerminate(void);
|
||||
void _RecorderReset(int nActuator);
|
||||
void _Record(int actuatorIndex, const char *format,...);
|
||||
#endif
|
||||
|
||||
/* Kernel Debug Macros */
|
||||
#ifdef __KERNEL__
|
||||
#ifdef VIBE_DEBUG
|
||||
#define DbgOut(_x_) printk _x_
|
||||
#else /* VIBE_DEBUG */
|
||||
#define DbgOut(_x_)
|
||||
#endif /* VIBE_DEBUG */
|
||||
|
||||
#if defined(VIBE_RECORD) && defined(VIBE_DEBUG)
|
||||
#define DbgRecorderInit(_x_) _RecorderInit _x_
|
||||
#define DbgRecorderTerminate(_x_) _RecorderTerminate _x_
|
||||
#define DbgRecorderReset(_x_) _RecorderReset _x_
|
||||
#define DbgRecord(_x_) _Record _x_
|
||||
#else /* defined(VIBE_RECORD) && defined(VIBE_DEBUG) */
|
||||
#define DbgRecorderInit(_x_)
|
||||
#define DbgRecorderTerminate(_x_)
|
||||
#define DbgRecorderReset(_x_)
|
||||
#define DbgRecord(_x_)
|
||||
#endif /* defined(VIBE_RECORD) && defined(VIBE_DEBUG) */
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
|
||||
#define __inp(port) ioread8(port)
|
||||
|
||||
#define __inpw(port) ioread16(port)
|
||||
|
@ -255,4 +160,4 @@ static void __iomem *virt_mmss_gp1_base;
|
|||
int vibrator_write_register(u8 addr, u8 w_data);
|
||||
|
||||
extern struct vibrator_platform_data_isa1200 vibrator_drvdata;
|
||||
#endif /* _TSPDRV_H */
|
||||
#endif
|
|
@ -28,58 +28,19 @@
|
|||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include "../staging/android/timed_output.h"
|
||||
#include <linux/isa1400_vibrator.h>
|
||||
#include <linux/fs.h>
|
||||
#include "tspdrv.h"
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/isa1400_vibrator.h>
|
||||
|
||||
#include "../staging/android/timed_output.h"
|
||||
#include "ss_vibrator.h"
|
||||
|
||||
#define MOTOR_DEBUG 0
|
||||
|
||||
#ifdef CONFIG_MOTOR_DRV_ISA1400
|
||||
#include "ImmVibeSPI_isa1400.c"
|
||||
#endif
|
||||
#define VERSION_STR " v3.4.55.7\n"
|
||||
/* account extra space for future extra digits in version number */
|
||||
#define VERSION_STR_LEN 16
|
||||
|
||||
static char g_szdevice_name[(VIBE_MAX_DEVICE_NAME_LENGTH
|
||||
+ VERSION_STR_LEN)
|
||||
* NUM_ACTUATORS];
|
||||
static size_t g_cchdevice_name;
|
||||
|
||||
static int vib_work_lock = 0;
|
||||
static struct wake_lock vib_wake_lock;
|
||||
|
||||
/* Flag indicating whether the driver is in use */
|
||||
static char g_bisplaying;
|
||||
|
||||
/* Buffer to store data sent to SPI */
|
||||
#define SPI_BUFFER_SIZE \
|
||||
(NUM_ACTUATORS * (VIBE_OUTPUT_SAMPLE_SIZE + SPI_HEADER_SIZE))
|
||||
static int g_bstoprequested;
|
||||
static actuator_samples_buffer g_samples_buffer[NUM_ACTUATORS] = {{0} };
|
||||
static char g_cwrite_buffer[SPI_BUFFER_SIZE];
|
||||
/* For QA purposes */
|
||||
#ifdef QA_TEST
|
||||
#define FORCE_LOG_BUFFER_SIZE 128
|
||||
#define TIME_INCREMENT 5
|
||||
static int g_ntime;
|
||||
static int g_nforcelog_index;
|
||||
static int8_t g_nforcelog[FORCE_LOG_BUFFER_SIZE];
|
||||
#endif
|
||||
|
||||
#ifdef IMPLEMENT_AS_CHAR_DRIVER
|
||||
static int g_nmajor;
|
||||
#endif
|
||||
|
||||
/* Needs to be included after the global variables because it uses them */
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
#include "VibeOSKernelLinuxHRTime.c"
|
||||
#else
|
||||
#include "VibeOSKernelLinuxTime.c"
|
||||
#endif
|
||||
|
||||
struct isa1400_vibrator_drvdata {
|
||||
struct timed_output_dev dev;
|
||||
struct hrtimer timer;
|
||||
|
@ -89,12 +50,13 @@ struct isa1400_vibrator_drvdata {
|
|||
struct isa1400_vibrator_platform_data *pdata;
|
||||
struct mutex mutex_lock;
|
||||
bool running;
|
||||
bool power_en;
|
||||
int timeout;
|
||||
int intensity;
|
||||
};
|
||||
|
||||
|
||||
static int isa1400_vibrator_i2c_write(struct i2c_client *client,
|
||||
u8 addr, u8 val)
|
||||
u8 addr, u8 val)
|
||||
{
|
||||
int error = 0;
|
||||
error = i2c_smbus_write_byte_data(client, addr, val);
|
||||
|
@ -104,14 +66,14 @@ static int isa1400_vibrator_i2c_write(struct i2c_client *client,
|
|||
|
||||
#if MOTOR_DEBUG
|
||||
printk(KERN_DEBUG "[VIB] %s : 0x%x, 0x%x\n",
|
||||
__func__, addr, val);
|
||||
__func__, addr, val);
|
||||
#endif
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void isa1400_reg_write(struct isa1400_vibrator_drvdata *data,
|
||||
u8 type)
|
||||
u8 type)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
|
||||
|
@ -119,11 +81,11 @@ static void isa1400_reg_write(struct isa1400_vibrator_drvdata *data,
|
|||
if (data->pdata->reg_data[i][0] == type){
|
||||
for (j = 2; j < data->pdata->reg_data[i][1]; j += 2){
|
||||
/*printk("[VIB] inside loop %d %d %d\n", j,
|
||||
data->pdata->reg_data[i][j],
|
||||
data->pdata->reg_data[i][j + 1]);*/
|
||||
data->pdata->reg_data[i][j],
|
||||
data->pdata->reg_data[i][j + 1]);*/
|
||||
isa1400_vibrator_i2c_write(data->client,
|
||||
data->pdata->reg_data[i][j],
|
||||
data->pdata->reg_data[i][j + 1]);
|
||||
data->pdata->reg_data[i][j],
|
||||
data->pdata->reg_data[i][j + 1]);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -137,8 +99,6 @@ static void isa1400_vibrator_hw_init(struct isa1400_vibrator_drvdata *data)
|
|||
isa1400_reg_write(data, ISA1400_REG_INIT);
|
||||
}
|
||||
|
||||
int vib_ioctl_lock = 0;
|
||||
EXPORT_SYMBOL(vib_ioctl_lock);
|
||||
static void isa1400_vibrator_on(struct isa1400_vibrator_drvdata *data)
|
||||
{
|
||||
isa1400_vibrator_hw_init(data);
|
||||
|
@ -147,10 +107,7 @@ static void isa1400_vibrator_on(struct isa1400_vibrator_drvdata *data)
|
|||
|
||||
static void isa1400_vibrator_off(struct isa1400_vibrator_drvdata *data)
|
||||
{
|
||||
#if 0
|
||||
isa1400_reg_write(data, ISA1400_REG_STOP);
|
||||
#endif
|
||||
if(vib_ioctl_lock)
|
||||
if(vib_work_lock)
|
||||
return;
|
||||
|
||||
isa1400_reg_write(data, ISA1400_REG_STOP);
|
||||
|
@ -169,18 +126,18 @@ int isa1400_i2c_write(u8 addr, int length, u8 *data)
|
|||
|
||||
if (2 != length)
|
||||
printk(KERN_ERR "[VIB] length should be 2(len:%d)\n",
|
||||
length);
|
||||
length);
|
||||
|
||||
#if MOTOR_DEBUG
|
||||
printk(KERN_DEBUG "[VIB] data : %x, %x\n",
|
||||
data[0], data[1]);
|
||||
data[0], data[1]);
|
||||
#endif
|
||||
|
||||
return isa1400_vibrator_i2c_write(g_drvdata->client,
|
||||
data[0], data[1]);
|
||||
data[0], data[1]);
|
||||
}
|
||||
|
||||
void isa1400_clk_config(u8 index, int duty)
|
||||
void isa1400_clk_config(int duty)
|
||||
{
|
||||
if (NULL == g_drvdata) {
|
||||
printk(KERN_ERR "[VIB] driver is not ready\n");
|
||||
|
@ -188,41 +145,32 @@ void isa1400_clk_config(u8 index, int duty)
|
|||
}
|
||||
|
||||
#if MOTOR_DEBUG
|
||||
printk(KERN_DEBUG "[VIB] %s %d, %d\n", __func__, index, duty);
|
||||
printk(KERN_DEBUG "[VIB] %s duty %d\n", __func__, duty);
|
||||
#endif
|
||||
|
||||
if (index >= g_drvdata->pdata->actuactor_num) {
|
||||
printk(KERN_ERR "[VIB] index is wrong : %d\n", index);
|
||||
return ;
|
||||
}
|
||||
|
||||
duty = (duty > 0) ? duty : (abs(duty) |0x80);
|
||||
duty = (duty > 0) ? duty : (abs(duty) | 0x80);
|
||||
|
||||
isa1400_vibrator_i2c_write(g_drvdata->client,
|
||||
ISA1400_REG_GAIN + g_drvdata->pdata->actuator[index],
|
||||
duty);
|
||||
ISA1400_REG_GAIN, duty);
|
||||
isa1400_vibrator_i2c_write(g_drvdata->client,
|
||||
ISA1400_REG_HPTEN,
|
||||
(0x01 << g_drvdata->pdata->actuator[index]));
|
||||
ISA1400_REG_HPTEN, 0x01);
|
||||
}
|
||||
|
||||
void isa1400_chip_enable(bool en)
|
||||
void vibe_set_intensity(int intensity)
|
||||
{
|
||||
if (NULL == g_drvdata) {
|
||||
printk(KERN_ERR "[VIB] driver is not ready\n");
|
||||
return ;
|
||||
if (!g_drvdata->running) {
|
||||
g_drvdata->pdata->gpio_en(true);
|
||||
}
|
||||
|
||||
if (en) {
|
||||
if (g_drvdata->power_en)
|
||||
return ;
|
||||
g_drvdata->power_en = true;
|
||||
isa1400_vibrator_hw_init(g_drvdata);
|
||||
} else {
|
||||
if (!g_drvdata->power_en)
|
||||
return ;
|
||||
g_drvdata->power_en = false;
|
||||
isa1400_vibrator_off(g_drvdata);
|
||||
if (0 == intensity)
|
||||
isa1400_clk_config(intensity);
|
||||
else {
|
||||
if (MAX_INTENSITY == intensity)
|
||||
intensity = 127;
|
||||
else if (0 != intensity) {
|
||||
int tmp = intensity;
|
||||
intensity = (tmp / 79); // 79 := 10000 / 127
|
||||
}
|
||||
isa1400_clk_config(intensity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,6 +201,7 @@ static void isa1400_vibrator_work(struct work_struct *_work)
|
|||
|
||||
data->running = true;
|
||||
isa1400_vibrator_on(data);
|
||||
vibe_set_intensity(data->intensity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,318 +233,46 @@ static void isa1400_vibrator_enable(struct timed_output_dev *_dev, int value)
|
|||
value = data->pdata->max_timeout;
|
||||
|
||||
hrtimer_start(&data->timer,
|
||||
ns_to_ktime((u64)value * NSEC_PER_MSEC),
|
||||
HRTIMER_MODE_REL);
|
||||
ns_to_ktime((u64)value * NSEC_PER_MSEC),
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
mutex_unlock(&data->mutex_lock);
|
||||
schedule_work(&data->work);
|
||||
}
|
||||
|
||||
/* File IO */
|
||||
static int open(struct inode *inode, struct file *file);
|
||||
static int release(struct inode *inode, struct file *file);
|
||||
static ssize_t read(struct file *file, char *buf, size_t count, loff_t *ppos);
|
||||
static ssize_t write(struct file *file, const char *buf, size_t count,
|
||||
loff_t *ppos);
|
||||
#if HAVE_UNLOCKED_IOCTL
|
||||
static long ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
|
||||
#endif
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = read,
|
||||
.write = write,
|
||||
.unlocked_ioctl = ioctl,
|
||||
.open = open,
|
||||
.release = release ,
|
||||
.llseek = default_llseek
|
||||
};
|
||||
|
||||
#ifndef IMPLEMENT_AS_CHAR_DRIVER
|
||||
static struct miscdevice miscdev = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = MODULE_NAME,
|
||||
.fops = &fops
|
||||
};
|
||||
#endif
|
||||
|
||||
static int open(struct inode *inode, struct file *file)
|
||||
static ssize_t intensity_store(struct device *dev,
|
||||
struct device_attribute *devattr, const char *buf, size_t count)
|
||||
{
|
||||
/*DbgOut((KERN_INFO "tspdrv: open.\n"));*/
|
||||
int ret = 0, set_intensity = 0;
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -ENODEV;
|
||||
ret = kstrtoint(buf, 0, &set_intensity);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int release(struct inode *inode, struct file *file)
|
||||
{
|
||||
DbgOut((KERN_INFO "tspdrv: release.\n"));
|
||||
|
||||
/*
|
||||
** Reset force and stop timer when the driver is closed, to make sure
|
||||
** no dangling semaphore remains in the system, especially when the
|
||||
** driver is run outside of immvibed for testing purposes.
|
||||
*/
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
|
||||
/*
|
||||
** Clear the variable used to store the magic number to prevent
|
||||
** unauthorized caller to write data. TouchSense service is the only
|
||||
** valid caller.
|
||||
*/
|
||||
file->private_data = (void *)NULL;
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t read(struct file *file, char *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
const size_t nbufsize =
|
||||
(g_cchdevice_name > (size_t)(*ppos)) ?
|
||||
min(count, g_cchdevice_name - (size_t)(*ppos)) : 0;
|
||||
/*DbgOut((KERN_INFO "tspdrv: read function called\n"));*/
|
||||
/* End of buffer, exit */
|
||||
if (0 == nbufsize)
|
||||
return 0;
|
||||
|
||||
if (0 != copy_to_user(buf, g_szdevice_name + (*ppos), nbufsize)) {
|
||||
/* Failed to copy all the data, exit */
|
||||
DbgOut((KERN_ERR "tspdrv: copy_to_user failed.\n"));
|
||||
return 0;
|
||||
if ((set_intensity < 0) || (set_intensity > MAX_INTENSITY)) {
|
||||
pr_err("[VIB]: %sout of rage\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Update file position and return copied buffer size */
|
||||
*ppos += nbufsize;
|
||||
return nbufsize;
|
||||
}
|
||||
|
||||
static ssize_t write(struct file *file, const char *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
*ppos = 0; /* file position not used, always set to 0 */
|
||||
/*DbgOut((KERN_ERR "tspdrv: write....\n")); */
|
||||
|
||||
/*
|
||||
** Prevent unauthorized caller to write data.
|
||||
** TouchSense service is the only valid caller.
|
||||
*/
|
||||
if (file->private_data != (void *)TSPDRV_MAGIC_NUMBER) {
|
||||
DbgOut((KERN_ERR "tspdrv: unauthorized write.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VIBRATOR_UPDATE
|
||||
/* Check buffer size */
|
||||
if ((count < SPI_HEADER_SIZE) || (count > SPI_BUFFER_SIZE)) {
|
||||
DbgOut((KERN_ERR "tspdrv: invalid write buffer size.\n"));
|
||||
return 0;
|
||||
}
|
||||
if (count == SPI_HEADER_SIZE)
|
||||
g_bOutputDataBufferEmpty = 1;
|
||||
else
|
||||
g_bOutputDataBufferEmpty = 0;
|
||||
|
||||
#else
|
||||
if ((count <= SPI_HEADER_SIZE) || (count > SPI_BUFFER_SIZE)) {
|
||||
DbgOut((KERN_ERR "tspdrv: invalid write buffer size.\n"));
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Copy immediately the input buffer */
|
||||
if (0 != copy_from_user(g_cwrite_buffer, buf, count)) {
|
||||
/* Failed to copy all the data, exit */
|
||||
DbgOut((KERN_ERR "tspdrv: copy_from_user failed.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (i < count) {
|
||||
int nindex_free_buffer; /* initialized below */
|
||||
|
||||
samples_buffer *pinput_buffer =
|
||||
(samples_buffer *)(&g_cwrite_buffer[i]);
|
||||
|
||||
#ifdef CONFIG_VIBRATOR_UPDATE
|
||||
if ((i + SPI_HEADER_SIZE) > count) {
|
||||
#else
|
||||
if ((i + SPI_HEADER_SIZE) >= count) {
|
||||
#endif
|
||||
/*
|
||||
** Index is about to go beyond the buffer size.
|
||||
** (Should never happen).
|
||||
*/
|
||||
DbgOut((KERN_EMERG "tspdrv: invalid buffer index.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check bit depth */
|
||||
if (8 != pinput_buffer->nbit_depth)
|
||||
DbgOut((KERN_WARNING
|
||||
"tspdrv: invalid bit depth.Use default value(8).\n"));
|
||||
|
||||
/* The above code not valid if SPI header size is not 3 */
|
||||
#if (SPI_HEADER_SIZE != 3)
|
||||
#error "SPI_HEADER_SIZE expected to be 3"
|
||||
#endif
|
||||
|
||||
/* Check buffer size */
|
||||
if ((i + SPI_HEADER_SIZE + pinput_buffer->nbuffer_size)
|
||||
> count) {
|
||||
/*
|
||||
** Index is about to go beyond the buffer size.
|
||||
** (Should never happen).
|
||||
*/
|
||||
DbgOut((KERN_EMERG "tspdrv: invalid data size.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check actuator index */
|
||||
if (NUM_ACTUATORS <= pinput_buffer->nactuator_index) {
|
||||
DbgOut((KERN_ERR "tspdrv: invalid actuator index.\n"));
|
||||
i += (SPI_HEADER_SIZE + pinput_buffer->nbuffer_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (0 == g_samples_buffer[pinput_buffer->nactuator_index]
|
||||
.actuator_samples[0].nbuffer_size) {
|
||||
nindex_free_buffer = 0;
|
||||
} else if (0 == g_samples_buffer[pinput_buffer->nactuator_index]
|
||||
.actuator_samples[1].nbuffer_size) {
|
||||
nindex_free_buffer = 1;
|
||||
} else {
|
||||
/* No room to store new samples */
|
||||
DbgOut((KERN_ERR
|
||||
"tspdrv: no room to store new samples.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Store the data in the free buffer of the given actuator */
|
||||
memcpy(
|
||||
&(g_samples_buffer[pinput_buffer->nactuator_index]
|
||||
.actuator_samples[nindex_free_buffer]),
|
||||
&g_cwrite_buffer[i],
|
||||
(SPI_HEADER_SIZE + pinput_buffer->nbuffer_size));
|
||||
|
||||
/* If the no buffer is playing, prepare to play
|
||||
** g_samples_buffer[pinput_buffer->nactuator_index].
|
||||
** actuator_samples[nindex_free_buffer]
|
||||
*/
|
||||
if (-1 == g_samples_buffer[pinput_buffer->nactuator_index]
|
||||
.nindex_playing_buffer) {
|
||||
g_samples_buffer[pinput_buffer->nactuator_index]
|
||||
.nindex_playing_buffer = nindex_free_buffer;
|
||||
g_samples_buffer[pinput_buffer->nactuator_index]
|
||||
.nindex_output_value = 0;
|
||||
}
|
||||
|
||||
/* Increment buffer index */
|
||||
i += (SPI_HEADER_SIZE + pinput_buffer->nbuffer_size);
|
||||
}
|
||||
|
||||
#ifdef QA_TEST
|
||||
g_nforcelog[g_nforcelog_index++] = g_cSPIBuffer[0];
|
||||
if (g_nforcelog_index >= FORCE_LOG_BUFFER_SIZE) {
|
||||
for (i = 0; i < FORCE_LOG_BUFFER_SIZE; i++) {
|
||||
printk(KERN_INFO "%d\t%d\n", g_ntime, g_nforcelog[i]);
|
||||
g_ntime += TIME_INCREMENT;
|
||||
}
|
||||
g_nforcelog_index = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Start the timer after receiving new output force */
|
||||
g_bisplaying = true;
|
||||
VibeOSKernelLinuxStartTimer();
|
||||
vibe_set_intensity(set_intensity);
|
||||
g_drvdata->intensity = set_intensity;
|
||||
|
||||
return count;
|
||||
}
|
||||
static long ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
|
||||
static ssize_t intensity_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
#ifdef QA_TEST
|
||||
int i;
|
||||
#endif
|
||||
|
||||
/*DbgOut(KERN_INFO "tspdrv: ioctl cmd[0x%x].\n", cmd); */
|
||||
switch (cmd) {
|
||||
case TSPDRV_STOP_KERNEL_TIMER:
|
||||
/*
|
||||
** As we send one sample ahead of time, we need to finish
|
||||
** playing the last sample before stopping the timer.
|
||||
** So we just set a flag here.
|
||||
*/
|
||||
if (g_bisplaying)
|
||||
g_bstoprequested = true;
|
||||
|
||||
#ifdef VIBEOSKERNELPROCESSDATA
|
||||
/* Last data processing to disable amp and stop timer */
|
||||
VibeOSKernelProcessData(NULL);
|
||||
#endif
|
||||
|
||||
#ifdef QA_TEST
|
||||
if (g_nforcelog_index) {
|
||||
for (i = 0; i < g_nforcelog_index; i++) {
|
||||
printk(KERN_INFO "%d\t%d\n"
|
||||
, g_ntime, g_nforcelog[i]);
|
||||
g_ntime += TIME_INCREMENT;
|
||||
}
|
||||
}
|
||||
g_ntime = 0;
|
||||
g_nforcelog_index = 0;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case TSPDRV_MAGIC_NUMBER:
|
||||
#ifdef CONFIG_VIBRATOR_UPDATE
|
||||
case TSPDRV_SET_MAGIC_NUMBER:
|
||||
#endif
|
||||
filp->private_data = (void *)TSPDRV_MAGIC_NUMBER;
|
||||
break;
|
||||
|
||||
case TSPDRV_ENABLE_AMP:
|
||||
wake_lock(&vib_wake_lock);
|
||||
ImmVibeSPI_ForceOut_AmpEnable(arg);
|
||||
DbgRecorderReset((arg));
|
||||
DbgRecord((arg, ";------- TSPDRV_ENABLE_AMP ---------\n"));
|
||||
break;
|
||||
|
||||
case TSPDRV_DISABLE_AMP:
|
||||
/*
|
||||
** Small fix for now to handle proper combination of
|
||||
** TSPDRV_STOP_KERNEL_TIMER and TSPDRV_DISABLE_AMP together
|
||||
** If a stop was requested, ignore the request as the amp
|
||||
** will be disabled by the timer proc when it's ready
|
||||
*/
|
||||
#ifdef CONFIG_VIBRATOR_UPDATE
|
||||
g_bstoprequested = true;
|
||||
/* Last data processing to disable amp and stop timer */
|
||||
VibeOSKernelProcessData(NULL);
|
||||
g_bisplaying = false;
|
||||
#else
|
||||
if (!g_bstoprequested)
|
||||
ImmVibeSPI_ForceOut_AmpDisable(arg);
|
||||
#endif
|
||||
wake_unlock(&vib_wake_lock);
|
||||
break;
|
||||
|
||||
case TSPDRV_GET_NUM_ACTUATORS:
|
||||
return NUM_ACTUATORS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return sprintf(buf, "intensity: %u\n", g_drvdata->intensity);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(intensity, 0660, intensity_show, intensity_store);
|
||||
|
||||
static int __devinit isa1400_vibrator_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct isa1400_vibrator_platform_data *pdata =
|
||||
client->dev.platform_data;
|
||||
struct isa1400_vibrator_drvdata *ddata;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
ddata = kzalloc(sizeof(struct isa1400_vibrator_drvdata), GFP_KERNEL);
|
||||
if (NULL == ddata) {
|
||||
|
@ -611,20 +288,6 @@ static int __devinit isa1400_vibrator_i2c_probe(struct i2c_client *client,
|
|||
} else
|
||||
ddata->pdata = pdata;
|
||||
|
||||
#ifdef IMPLEMENT_AS_CHAR_DRIVER
|
||||
g_nmajor = register_chrdev(0, MODULE_NAME, &fops);
|
||||
if (g_nmajor < 0) {
|
||||
DbgOut((KERN_ERR "tspdrv: can't get major number.\n"));
|
||||
ret = g_nmajor;
|
||||
goto err_platform_data;
|
||||
}
|
||||
#else
|
||||
ret = misc_register(&miscdev);
|
||||
if (ret) {
|
||||
DbgOut((KERN_ERR "tspdrv: misc_register failed.\n"));
|
||||
goto err_platform_data;
|
||||
}
|
||||
#endif
|
||||
ddata->dev.name = "vibrator";
|
||||
ddata->dev.get_time = isa1400_vibrator_get_time;
|
||||
ddata->dev.enable = isa1400_vibrator_enable;
|
||||
|
@ -634,32 +297,25 @@ static int __devinit isa1400_vibrator_i2c_probe(struct i2c_client *client,
|
|||
INIT_WORK(&ddata->work, isa1400_vibrator_work);
|
||||
mutex_init(&ddata->mutex_lock);
|
||||
|
||||
ddata->intensity = MAX_INTENSITY;
|
||||
|
||||
i2c_set_clientdata(client, ddata);
|
||||
/*ddata->pdata->gpio_en(true);
|
||||
isa1400_vibrator_hw_init(ddata);*/
|
||||
VibeOSKernelLinuxInitTimer();
|
||||
/* Get and concatenate device name and initialize data buffer */
|
||||
g_cchdevice_name = 0;
|
||||
for (i = 0; i < NUM_ACTUATORS; i++) {
|
||||
char *szName = g_szdevice_name + g_cchdevice_name;
|
||||
ImmVibeSPI_Device_GetName(i,
|
||||
szName, VIBE_MAX_DEVICE_NAME_LENGTH);
|
||||
|
||||
/* Append version information and get buffer length */
|
||||
strlcat(szName, VERSION_STR, sizeof(VERSION_STR));
|
||||
g_cchdevice_name += strnlen(szName, sizeof(szName));
|
||||
|
||||
g_samples_buffer[i].nindex_playing_buffer = -1;/* Not playing */
|
||||
g_samples_buffer[i].actuator_samples[0].nbuffer_size = 0;
|
||||
g_samples_buffer[i].actuator_samples[1].nbuffer_size = 0;
|
||||
}
|
||||
isa1400_vibrator_hw_init(ddata);*/
|
||||
wake_lock_init(&vib_wake_lock, WAKE_LOCK_SUSPEND, "vib_present");
|
||||
ret = timed_output_dev_register(&ddata->dev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "[VIB] Failed to register timed_output : -%d\n", ret);
|
||||
goto err_to_dev_reg;
|
||||
}
|
||||
|
||||
ret = sysfs_create_file(&ddata->dev.dev->kobj, &dev_attr_intensity.attr);
|
||||
if (ret < 0) {
|
||||
pr_err("[VIB]: Failed to register sysfs intensity: %d\n", ret);
|
||||
}
|
||||
|
||||
g_drvdata = ddata;
|
||||
|
||||
return 0;
|
||||
|
||||
err_to_dev_reg:
|
||||
|
@ -675,8 +331,8 @@ static int isa1400_vibrator_suspend(struct i2c_client *client, pm_message_t mesg
|
|||
struct isa1400_vibrator_drvdata *ddata = i2c_get_clientdata(client);
|
||||
printk(KERN_INFO "[VIB] isa1400_vibrator_suspend called ");
|
||||
|
||||
if(vib_ioctl_lock){
|
||||
vib_ioctl_lock = 0;
|
||||
if(vib_work_lock){
|
||||
vib_work_lock = 0;
|
||||
ddata->pdata->gpio_en(false);
|
||||
ddata->pdata->clk_en(false);
|
||||
}
|
||||
|
@ -700,10 +356,10 @@ static struct i2c_driver isa1400_vibrator_i2c_driver = {
|
|||
.name = "isa1400_vibrator",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = isa1400_vibrator_i2c_probe,
|
||||
.id_table = isa1400_vibrator_device_id,
|
||||
.probe = isa1400_vibrator_i2c_probe,
|
||||
.id_table = isa1400_vibrator_device_id,
|
||||
.suspend = isa1400_vibrator_suspend,
|
||||
.resume = isa1400_vibrator_resume,
|
||||
.resume = isa1400_vibrator_resume,
|
||||
};
|
||||
|
||||
static int __init isa1400_vibrator_init(void)
|
||||
|
@ -714,17 +370,16 @@ static int __init isa1400_vibrator_init(void)
|
|||
|
||||
static void __exit isa1400_vibrator_exit(void)
|
||||
{
|
||||
VibeOSKernelLinuxTerminateTimer();
|
||||
wake_lock_destroy(&vib_wake_lock);
|
||||
mutex_destroy(&g_drvdata->mutex_lock);
|
||||
timed_output_dev_unregister(&g_drvdata->dev);
|
||||
mutex_destroy(&g_drvdata->mutex_lock);
|
||||
timed_output_dev_unregister(&g_drvdata->dev);
|
||||
i2c_del_driver(&isa1400_vibrator_i2c_driver);
|
||||
}
|
||||
|
||||
module_init(isa1400_vibrator_init);
|
||||
module_exit(isa1400_vibrator_exit);
|
||||
|
||||
MODULE_AUTHOR("Junki Min <Junki671.min@samsung.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("haptic driver for the isa1400 ic");
|
||||
MODULE_AUTHOR("Samsung Corporation");
|
||||
MODULE_DESCRIPTION("Vibrator driver for the isa1400 ic");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ static void max77803_haptic_i2c(struct max77803_haptic_data *hap_data, bool en)
|
|||
pr_err("[VIB] i2c write error %d\n", ret);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VIBETONZ
|
||||
#ifdef CONFIG_SS_VIBRATOR
|
||||
void max77803_vibtonz_en(bool en)
|
||||
{
|
||||
if (g_hap_data == NULL) {
|
||||
|
@ -90,7 +90,7 @@ static int max77803_haptic_probe(struct platform_device *pdev)
|
|||
struct max77803_dev *max77803 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct max77803_platform_data *max77803_pdata
|
||||
= dev_get_platdata(max77803->dev);
|
||||
#ifdef CONFIG_VIBETONZ
|
||||
#ifdef CONFIG_SS_VIBRATOR
|
||||
struct max77803_haptic_platform_data *pdata
|
||||
= max77803_pdata->haptic_data;
|
||||
#endif
|
||||
|
@ -113,6 +113,7 @@ static int max77803_haptic_probe(struct platform_device *pdev)
|
|||
hap_data->pmic_i2c = max77803->i2c;
|
||||
pdata->reg2 = MOTOR_LRA | EXT_PWM | DIVIDER_128;
|
||||
hap_data->pdata = pdata;
|
||||
max77803_haptic_i2c(hap_data, true);
|
||||
|
||||
spin_lock_init(&(hap_data->lock));
|
||||
|
||||
|
@ -130,6 +131,19 @@ static int __devexit max77803_haptic_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void max77803_haptic_shutdown(struct device *dev)
|
||||
{
|
||||
struct max77803_haptic_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
pr_info("%s: Disable HAPTIC\n", __func__);
|
||||
ret = max77803_update_reg(data->i2c, MAX77803_HAPTIC_REG_CONFIG2, 0x0, MOTOR_EN);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: fail to update reg\n", __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int max77803_haptic_suspend(struct platform_device *pdev,
|
||||
pm_message_t state)
|
||||
{
|
||||
|
@ -148,6 +162,7 @@ static struct platform_driver max77803_haptic_driver = {
|
|||
.driver = {
|
||||
.name = "max77803-haptic",
|
||||
.owner = THIS_MODULE,
|
||||
.shutdown = max77803_haptic_shutdown,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ static void max77804k_haptic_i2c(struct max77804k_haptic_data *hap_data, bool en
|
|||
pr_err("[VIB] i2c write error %d\n", ret);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VIBETONZ
|
||||
#ifdef CONFIG_SS_VIBRATOR
|
||||
void max77804k_vibtonz_en(bool en)
|
||||
{
|
||||
if (g_hap_data == NULL) {
|
||||
|
@ -91,7 +91,7 @@ static int __devinit max77804k_haptic_probe(struct platform_device *pdev)
|
|||
struct max77804k_platform_data *max77804k_pdata
|
||||
= dev_get_platdata(max77804k->dev);
|
||||
|
||||
#ifdef CONFIG_VIBETONZ
|
||||
#ifdef CONFIG_SS_VIBRATOR
|
||||
struct max77804k_haptic_platform_data *pdata
|
||||
= max77804k_pdata->haptic_data;
|
||||
#endif
|
||||
|
@ -114,6 +114,7 @@ static int __devinit max77804k_haptic_probe(struct platform_device *pdev)
|
|||
hap_data->pmic_i2c = max77804k->i2c;
|
||||
pdata->reg2 = MOTOR_LRA | EXT_PWM | DIVIDER_128;
|
||||
hap_data->pdata = pdata;
|
||||
max77804k_haptic_i2c(hap_data, true);
|
||||
|
||||
spin_lock_init(&(hap_data->lock));
|
||||
|
||||
|
@ -131,6 +132,19 @@ static int __devexit max77804k_haptic_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void max77804k_haptic_shutdown(struct device *dev)
|
||||
{
|
||||
struct max77804k_haptic_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
pr_info("%s: Disable HAPTIC\n", __func__);
|
||||
ret = max77804k_update_reg(data->i2c, MAX77804K_HAPTIC_REG_CONFIG2, 0x0, MOTOR_EN);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: fail to update reg\n", __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int max77804k_haptic_suspend(struct platform_device *pdev,
|
||||
pm_message_t state)
|
||||
{
|
||||
|
@ -149,6 +163,7 @@ static struct platform_driver max77804k_haptic_driver = {
|
|||
.driver = {
|
||||
.name = "max77804k-haptic",
|
||||
.owner = THIS_MODULE,
|
||||
.shutdown = max77804k_haptic_shutdown,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ static void max77828_haptic_i2c(struct max77828_haptic_data *hap_data, bool en)
|
|||
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VIBETONZ
|
||||
#ifdef CONFIG_SS_VIBRATOR
|
||||
void max77828_vibtonz_en(bool en)
|
||||
{
|
||||
if (g_hap_data == NULL) {
|
||||
|
@ -85,7 +85,7 @@ static int __devinit max77828_haptic_probe(struct platform_device *pdev)
|
|||
struct max77828_platform_data *max77828_pdata
|
||||
= dev_get_platdata(max77828->dev);
|
||||
|
||||
#ifdef CONFIG_VIBETONZ
|
||||
#ifdef CONFIG_SS_VIBRATOR
|
||||
struct max77828_haptic_platform_data *pdata
|
||||
= max77828_pdata->haptic_data;
|
||||
#endif
|
||||
|
@ -107,6 +107,7 @@ static int __devinit max77828_haptic_probe(struct platform_device *pdev)
|
|||
hap_data->i2c = max77828->i2c;
|
||||
hap_data->pdata = pdata;
|
||||
platform_set_drvdata(pdev, hap_data);
|
||||
max77828_haptic_i2c(hap_data, true);
|
||||
|
||||
spin_lock_init(&(hap_data->lock));
|
||||
|
||||
|
@ -124,6 +125,19 @@ static int __devexit max77828_haptic_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void max77828_haptic_shutdown(struct device *dev)
|
||||
{
|
||||
struct max77828_haptic_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
pr_info("%s: Disable HAPTIC\n", __func__);
|
||||
ret = max77828_update_reg(data->i2c, MAX77828_PMIC_REG_MCONFIG, 0x0, MOTOR_EN);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: fail to update reg\n", __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int max77828_haptic_suspend(struct platform_device *pdev,
|
||||
pm_message_t state)
|
||||
{
|
||||
|
@ -142,6 +156,7 @@ static struct platform_driver max77828_haptic_driver = {
|
|||
.driver = {
|
||||
.name = "max77828-haptic",
|
||||
.owner = THIS_MODULE,
|
||||
.shutdown = max77828_haptic_shutdown,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ static void max77888_haptic_i2c(struct max77888_haptic_data *hap_data, bool en)
|
|||
pr_err("[VIB] i2c write error %d\n", ret);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VIBETONZ
|
||||
#ifdef CONFIG_SS_VIBRATOR
|
||||
void max77888_vibtonz_en(bool en)
|
||||
{
|
||||
if (g_hap_data == NULL) {
|
||||
|
@ -91,7 +91,7 @@ static int __devinit max77888_haptic_probe(struct platform_device *pdev)
|
|||
struct max77888_platform_data *max77888_pdata
|
||||
= dev_get_platdata(max77888->dev);
|
||||
|
||||
#ifdef CONFIG_VIBETONZ
|
||||
#ifdef CONFIG_SS_VIBRATOR
|
||||
struct max77888_haptic_platform_data *pdata
|
||||
= max77888_pdata->haptic_data;
|
||||
#endif
|
||||
|
@ -114,6 +114,7 @@ static int __devinit max77888_haptic_probe(struct platform_device *pdev)
|
|||
hap_data->pmic_i2c = max77888->i2c;
|
||||
pdata->reg2 = MOTOR_LRA | EXT_PWM | DIVIDER_128;
|
||||
hap_data->pdata = pdata;
|
||||
max77888_haptic_i2c(hap_data, true);
|
||||
|
||||
spin_lock_init(&(hap_data->lock));
|
||||
|
||||
|
@ -131,6 +132,19 @@ static int __devexit max77888_haptic_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void max77888_haptic_shutdown(struct device *dev)
|
||||
{
|
||||
struct max77888_haptic_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
pr_info("%s: Disable HAPTIC\n", __func__);
|
||||
ret = max77888_update_reg(data->i2c, MAX77888_HAPTIC_REG_CONFIG2, 0x0, MOTOR_EN);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: fail to update reg\n", __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int max77888_haptic_suspend(struct platform_device *pdev,
|
||||
pm_message_t state)
|
||||
{
|
||||
|
@ -149,6 +163,7 @@ static struct platform_driver max77888_haptic_driver = {
|
|||
.driver = {
|
||||
.name = "max77888-haptic",
|
||||
.owner = THIS_MODULE,
|
||||
.shutdown = max77888_haptic_shutdown,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,908 @@
|
|||
|
||||
/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only 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/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/vibrator.h>
|
||||
#include <mach/msm_iomap.h>
|
||||
#include <linux/mfd/pm8xxx/pwm.h>
|
||||
#include <linux/mfd/pm8xxx/pm8921.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include "../staging/android/timed_output.h"
|
||||
|
||||
#include "ss_vibrator.h"
|
||||
|
||||
/* default timeout */
|
||||
#define VIB_DEFAULT_TIMEOUT 10000
|
||||
|
||||
struct pm_qos_request pm_qos_req;
|
||||
static struct wake_lock vib_wake_lock;
|
||||
|
||||
struct vibrator_platform_data vibrator_drvdata;
|
||||
|
||||
struct ss_vib {
|
||||
struct device *dev;
|
||||
struct hrtimer vib_timer;
|
||||
struct timed_output_dev timed_dev;
|
||||
struct work_struct work;
|
||||
struct workqueue_struct *queue;
|
||||
struct mutex lock;
|
||||
|
||||
int state;
|
||||
int timeout;
|
||||
int intensity;
|
||||
int timevalue;
|
||||
};
|
||||
|
||||
void vibe_set_intensity(int intensity)
|
||||
{
|
||||
if (0 == intensity)
|
||||
vibe_pwm_onoff(0);
|
||||
else {
|
||||
if (MAX_INTENSITY == intensity)
|
||||
intensity = 1;
|
||||
else if (0 != intensity) {
|
||||
int tmp = MAX_INTENSITY - intensity;
|
||||
intensity = (tmp / 79); // 79 := 10000 / 127
|
||||
}
|
||||
|
||||
if (vibrator_drvdata.is_pmic_vib_pwm){
|
||||
//PMIC PWM
|
||||
if (vib_config_pwm_device() < 0)
|
||||
pr_err("%s vib_config_pwm_device failed\n", __func__);
|
||||
} else {
|
||||
vibe_set_pwm_freq(intensity);
|
||||
vibe_pwm_onoff(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t vibe_set_pwm_freq(int intensity)
|
||||
{
|
||||
int32_t calc_d;
|
||||
|
||||
/* Put the MND counter in reset mode for programming */
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_SEL_VAL_BMSK,
|
||||
0 << HWIO_GP_SRC_SEL_VAL_SHFT); //SRC_SEL = 000(cxo)
|
||||
#if defined(CONFIG_SEC_BERLUTI_PROJECT) || defined(CONFIG_MACH_S3VE3G_EUR)
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_DIV_VAL_BMSK,
|
||||
23 << HWIO_GP_SRC_DIV_VAL_SHFT); //SRC_DIV = 10111 (Div 12)
|
||||
#else
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_SRC_DIV_VAL_BMSK,
|
||||
31 << HWIO_GP_SRC_DIV_VAL_SHFT); //SRC_DIV = 11111 (Div 16)
|
||||
#endif
|
||||
HWIO_OUTM(GP1_CFG_RCGR, HWIO_GP_MODE_VAL_BMSK,
|
||||
2 << HWIO_GP_MODE_VAL_SHFT); //Mode Select 10
|
||||
//M value
|
||||
HWIO_OUTM(GP_M_REG, HWIO_GP_MD_REG_M_VAL_BMSK,
|
||||
g_nlra_gp_clk_m << HWIO_GP_MD_REG_M_VAL_SHFT);
|
||||
|
||||
#if defined(CONFIG_MACH_LT03EUR) || defined(CONFIG_MACH_LT03SKT)\
|
||||
|| defined(CONFIG_MACH_LT03KTT) || defined(CONFIG_MACH_LT03LGT) || defined(CONFIG_MACH_PICASSO_LTE)
|
||||
|
||||
if (intensity > 0){
|
||||
calc_d = g_nlra_gp_clk_n - (((intensity * g_nlra_gp_clk_pwm_mul) >> 8));
|
||||
if(calc_d < motor_min_strength)
|
||||
calc_d = motor_min_strength;
|
||||
else
|
||||
calc_d = (calc_d - motor_min_strength) \
|
||||
* (g_nlra_gp_clk_n * motor_strength / 100 - motor_min_strength) \
|
||||
/ (g_nlra_gp_clk_n - motor_min_strength) + motor_min_strength;
|
||||
}
|
||||
else{
|
||||
calc_d = ((intensity * g_nlra_gp_clk_pwm_mul) >> 8) + g_nlra_gp_clk_d;
|
||||
if(g_nlra_gp_clk_n - calc_d > g_nlra_gp_clk_n * motor_strength /100)
|
||||
calc_d = g_nlra_gp_clk_n - g_nlra_gp_clk_n * motor_strength /100;
|
||||
}
|
||||
#else
|
||||
if (intensity > 0){
|
||||
calc_d = g_nlra_gp_clk_n - (((intensity * g_nlra_gp_clk_pwm_mul) >> 8));
|
||||
calc_d = calc_d * motor_strength /100;
|
||||
if(calc_d < motor_min_strength)
|
||||
calc_d = motor_min_strength;
|
||||
}
|
||||
else{
|
||||
calc_d = ((intensity * g_nlra_gp_clk_pwm_mul) >> 8) + g_nlra_gp_clk_d;
|
||||
if(g_nlra_gp_clk_n - calc_d > g_nlra_gp_clk_n * motor_strength /100)
|
||||
calc_d = g_nlra_gp_clk_n - g_nlra_gp_clk_n * motor_strength /100;
|
||||
}
|
||||
#endif
|
||||
// D value
|
||||
HWIO_OUTM(GP_D_REG, HWIO_GP_MD_REG_D_VAL_BMSK,
|
||||
(~((int16_t)calc_d << 1)) << HWIO_GP_MD_REG_D_VAL_SHFT);
|
||||
|
||||
//N value
|
||||
HWIO_OUTM(GP_NS_REG, HWIO_GP_NS_REG_GP_N_VAL_BMSK,
|
||||
~(g_nlra_gp_clk_n - g_nlra_gp_clk_m) << 0);
|
||||
|
||||
return VIBRATION_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t vibe_pwm_onoff(u8 onoff)
|
||||
{
|
||||
if (onoff) {
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_UPDATE_VAL_BMSK,
|
||||
1 << HWIO_UPDATE_VAL_SHFT);//UPDATE ACTIVE
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_ROOT_EN_VAL_BMSK,
|
||||
1 << HWIO_ROOT_EN_VAL_SHFT);//ROOT_EN
|
||||
HWIO_OUTM(CAMSS_GP1_CBCR, HWIO_CLK_ENABLE_VAL_BMSK,
|
||||
1 << HWIO_CLK_ENABLE_VAL_SHFT); //CLK_ENABLE
|
||||
} else {
|
||||
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_UPDATE_VAL_BMSK,
|
||||
0 << HWIO_UPDATE_VAL_SHFT);
|
||||
HWIO_OUTM(GP1_CMD_RCGR,HWIO_ROOT_EN_VAL_BMSK,
|
||||
0 << HWIO_ROOT_EN_VAL_SHFT);
|
||||
HWIO_OUTM(CAMSS_GP1_CBCR, HWIO_CLK_ENABLE_VAL_BMSK,
|
||||
0 << HWIO_CLK_ENABLE_VAL_SHFT);
|
||||
}
|
||||
return VIBRATION_SUCCESS;
|
||||
}
|
||||
|
||||
int vib_config_pwm_device(void)
|
||||
{
|
||||
int ret = 0;
|
||||
if(vibrator_drvdata.pwm_dev == NULL){
|
||||
//u32 pwm_period_us, duty_us;
|
||||
#if defined(CONFIG_MACH_HLTEDCM) || defined(CONFIG_MACH_HLTEKDI) || \
|
||||
defined(CONFIG_MACH_JS01LTEDCM) || defined(CONFIG_MACH_JS01LTESBM)
|
||||
vibrator_drvdata.pwm_dev = pwm_request(0,"lpg_3"); // 0 index for LPG3 channel.
|
||||
#else
|
||||
vibrator_drvdata.pwm_dev = pwm_request(0,"lpg_1"); // 0 index for LPG1 channel.
|
||||
#endif
|
||||
|
||||
if (IS_ERR_OR_NULL(vibrator_drvdata.pwm_dev)) {
|
||||
pr_err("could not acquire PWM Channel 0, "
|
||||
"error %ld\n",PTR_ERR(vibrator_drvdata.pwm_dev));
|
||||
vibrator_drvdata.pwm_dev = NULL;
|
||||
return -ENODEV;
|
||||
}
|
||||
//pwm_period_us = 19; // 2000000;
|
||||
//duty_us = 18; //1000000; (90% Duty Cycle)
|
||||
|
||||
ret = pwm_config(vibrator_drvdata.pwm_dev,
|
||||
vibrator_drvdata.duty_us,
|
||||
vibrator_drvdata.pwm_period_us);
|
||||
if (ret) {
|
||||
pr_err("pwm_config in vibrator enable failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = pwm_enable(vibrator_drvdata.pwm_dev);
|
||||
if (ret < 0) {
|
||||
pr_err("pwm_enable in vibrator failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = pwm_enable(vibrator_drvdata.pwm_dev);
|
||||
if (ret < 0) {
|
||||
pr_err("pwm_enable in vibrator failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void set_vibrator(struct ss_vib *vib)
|
||||
{
|
||||
pr_info("[VIB]: %s, value[%d]\n", __func__, vib->state);
|
||||
if (vib->state) {
|
||||
wake_lock(&vib_wake_lock);
|
||||
pm_qos_update_request(&pm_qos_req, PM_QOS_NONIDLE_VALUE);
|
||||
if(vibrator_drvdata.is_pmic_vib_pwm){ //PMIC PWM
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_ON);
|
||||
} else { //AP PWM
|
||||
#if defined(CONFIG_MACH_HLTEDCM) || defined(CONFIG_MACH_HLTEKDI) || \
|
||||
defined(CONFIG_MACH_JS01LTEDCM) || defined(CONFIG_MACH_JS01LTESBM)
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,\
|
||||
2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, \
|
||||
GPIO_CFG_2MA), GPIO_CFG_ENABLE);
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_ON);
|
||||
#elif defined(CONFIG_SEC_BERLUTI_PROJECT) || defined(CONFIG_MACH_S3VE3G_EUR) || defined(CONFIG_MACH_VICTOR3GDSDTV_LTN) || defined(CONFIG_SEC_HESTIA_PROJECT)
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,\
|
||||
3, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, \
|
||||
GPIO_CFG_2MA), GPIO_CFG_ENABLE);
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_ON);
|
||||
#else
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,\
|
||||
6, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, \
|
||||
GPIO_CFG_2MA), GPIO_CFG_ENABLE);
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_ON);
|
||||
#endif
|
||||
}
|
||||
printk(KERN_DEBUG "[VIB] : %s\n", __func__);
|
||||
if (vibrator_drvdata.power_onoff) {
|
||||
if (!vibrator_drvdata.changed_chip)
|
||||
vibrator_drvdata.power_onoff(1);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MOTOR_DRV_MAX77804K)
|
||||
if (vibrator_drvdata.changed_chip) {
|
||||
gpio_direction_output(vibrator_drvdata.changed_en_gpio, VIBRATION_ON);
|
||||
gpio_set_value(vibrator_drvdata.changed_en_gpio,VIBRATION_ON);
|
||||
}
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77888)
|
||||
max77888_gpio_en(1);
|
||||
#elif defined(CONFIG_MOTOR_DRV_DRV2603)
|
||||
drv2603_gpio_en(1);
|
||||
#elif defined(CONFIG_MOTOR_ISA1000)
|
||||
gpio_direction_output(vibrator_drvdata.vib_en_gpio,VIBRATION_ON);
|
||||
gpio_set_value(vibrator_drvdata.vib_en_gpio,VIBRATION_ON);
|
||||
#endif
|
||||
hrtimer_start(&vib->vib_timer, ktime_set(vib->timevalue / 1000, (vib->timevalue % 1000) * 1000000),HRTIMER_MODE_REL);
|
||||
} else {
|
||||
if(vibrator_drvdata.is_pmic_vib_pwm){ //PMIC PWM
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_OFF);
|
||||
if(vibrator_drvdata.pwm_dev != NULL) //Disable the PWM device.
|
||||
pwm_disable(vibrator_drvdata.pwm_dev);
|
||||
} else{ //AP PWM
|
||||
#if defined(CONFIG_MACH_S3VE3G_EUR) || defined(CONFIG_MACH_VICTOR3GDSDTV_LTN)
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,\
|
||||
0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, \
|
||||
GPIO_CFG_2MA),GPIO_CFG_ENABLE);
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_OFF);
|
||||
#else
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,\
|
||||
0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, \
|
||||
GPIO_CFG_2MA),GPIO_CFG_ENABLE);
|
||||
gpio_set_value(vibrator_drvdata.vib_pwm_gpio, \
|
||||
VIBRATION_OFF);
|
||||
#endif
|
||||
}
|
||||
printk(KERN_DEBUG "[VIB] : %s\n", __func__);
|
||||
if (vibrator_drvdata.power_onoff) {
|
||||
if (!vibrator_drvdata.changed_chip)
|
||||
vibrator_drvdata.power_onoff(0);
|
||||
}
|
||||
#if defined(CONFIG_MOTOR_DRV_MAX77804K)
|
||||
if (vibrator_drvdata.changed_chip) {
|
||||
gpio_direction_output(vibrator_drvdata.changed_en_gpio, VIBRATION_OFF);
|
||||
gpio_set_value(vibrator_drvdata.changed_en_gpio,VIBRATION_OFF);
|
||||
}
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77888)
|
||||
max77888_gpio_en(0);
|
||||
#elif defined(CONFIG_MOTOR_DRV_DRV2603)
|
||||
drv2603_gpio_en(0);
|
||||
#elif defined(CONFIG_MOTOR_ISA1000)
|
||||
gpio_direction_output(vibrator_drvdata.vib_en_gpio,VIBRATION_OFF);
|
||||
gpio_set_value(vibrator_drvdata.vib_en_gpio,VIBRATION_OFF);
|
||||
#endif
|
||||
wake_unlock(&vib_wake_lock);
|
||||
pm_qos_update_request(&pm_qos_req, PM_QOS_DEFAULT_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
static void vibrator_enable(struct timed_output_dev *dev, int value)
|
||||
{
|
||||
struct ss_vib *vib = container_of(dev, struct ss_vib, timed_dev);
|
||||
|
||||
mutex_lock(&vib->lock);
|
||||
hrtimer_cancel(&vib->vib_timer);
|
||||
|
||||
if (value == 0) {
|
||||
pr_info("[VIB]: OFF\n");
|
||||
vib->state = 0;
|
||||
} else {
|
||||
pr_info("[VIB]: ON, Duration : %d msec, intensity : %d\n", value, vib->intensity);
|
||||
vib->state = 1;
|
||||
vib->timevalue = value;
|
||||
}
|
||||
mutex_unlock(&vib->lock);
|
||||
queue_work(vib->queue, &vib->work);
|
||||
}
|
||||
|
||||
static void ss_vibrator_update(struct work_struct *work)
|
||||
{
|
||||
struct ss_vib *vib = container_of(work, struct ss_vib, work);
|
||||
|
||||
set_vibrator(vib);
|
||||
}
|
||||
|
||||
static int vibrator_get_time(struct timed_output_dev *dev)
|
||||
{
|
||||
struct ss_vib *vib = container_of(dev, struct ss_vib, timed_dev);
|
||||
|
||||
if (hrtimer_active(&vib->vib_timer)) {
|
||||
ktime_t r = hrtimer_get_remaining(&vib->vib_timer);
|
||||
return (int)ktime_to_us(r);
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
|
||||
{
|
||||
struct ss_vib *vib = container_of(timer, struct ss_vib, vib_timer);
|
||||
|
||||
vib->state = 0;
|
||||
queue_work(vib->queue, &vib->work);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ss_vibrator_suspend(struct device *dev)
|
||||
{
|
||||
struct ss_vib *vib = dev_get_drvdata(dev);
|
||||
|
||||
pr_info("[VIB]: %s\n", __func__);
|
||||
|
||||
hrtimer_cancel(&vib->vib_timer);
|
||||
cancel_work_sync(&vib->work);
|
||||
/* turn-off vibrator */
|
||||
vib->state = 0;
|
||||
set_vibrator(vib);
|
||||
|
||||
#if defined(CONFIG_MOTOR_DRV_MAX77803)
|
||||
max77803_vibtonz_en(0);
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77804K)
|
||||
if (!vibrator_drvdata.changed_chip)
|
||||
max77804k_vibtonz_en(0);
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77828)
|
||||
max77828_vibtonz_en(0);
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77888)
|
||||
max77888_vibtonz_en(0);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ss_vibrator_resume(struct device *dev)
|
||||
{
|
||||
struct ss_vib *vib = dev_get_drvdata(dev);
|
||||
|
||||
pr_info("[VIB]: %s, intensity : %d\n", __func__, vib->intensity);
|
||||
|
||||
#if defined(CONFIG_MOTOR_DRV_MAX77803)
|
||||
max77803_vibtonz_en(1);
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77804K)
|
||||
if (!vibrator_drvdata.changed_chip)
|
||||
max77804k_vibtonz_en(1);
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77828)
|
||||
max77828_vibtonz_en(1);
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77888)
|
||||
max77888_vibtonz_en(1);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(vibrator_pm_ops, ss_vibrator_suspend, ss_vibrator_resume);
|
||||
|
||||
static int vibrator_parse_dt(struct ss_vib *vib)
|
||||
{
|
||||
struct device_node *np = vib->dev->of_node;
|
||||
int rc;
|
||||
|
||||
#if defined(CONFIG_MACH_HLTEDCM) || defined(CONFIG_MACH_HLTEKDI) || defined (CONFIG_MACH_JS01LTEDCM)
|
||||
vibrator_drvdata.vib_pwm_gpio = of_get_named_gpio(np, "samsung,pmic_vib_pwm_jpn", 0);
|
||||
#else
|
||||
vibrator_drvdata.vib_pwm_gpio = of_get_named_gpio(np, "samsung,pmic_vib_pwm", 0);
|
||||
#endif
|
||||
|
||||
if (!gpio_is_valid(vibrator_drvdata.vib_pwm_gpio)) {
|
||||
pr_err("%s:%d, reset gpio not specified\n",
|
||||
__func__, __LINE__);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MOTOR_ISA1000)
|
||||
vibrator_drvdata.vib_en_gpio = of_get_named_gpio(np, "samsung,vib_en_gpio", 0);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MOTOR_DRV_DRV2603)
|
||||
vibrator_drvdata.drv2603_en_gpio = of_get_named_gpio(np, "samsung,drv2603_en", 0);
|
||||
if (!gpio_is_valid(vibrator_drvdata.drv2603_en_gpio)) {
|
||||
pr_err("%s:%d, drv2603_en_gpio not specified\n",
|
||||
__func__, __LINE__);
|
||||
}
|
||||
#endif
|
||||
#if defined(CONFIG_MOTOR_DRV_MAX77888)
|
||||
vibrator_drvdata.max77888_en_gpio = of_get_named_gpio(np, "samsung,vib_power_en", 0);
|
||||
if (!gpio_is_valid(vibrator_drvdata.max77888_en_gpio)) {
|
||||
pr_err("%s:%d, max77888_en_gpio not specified\n",__func__, __LINE__);
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = of_property_read_u32(np, "samsung,pmic_vib_en", &vibrator_drvdata.is_pmic_vib_en);
|
||||
if (rc) {
|
||||
pr_err("%s:%d, is_pmic_vib_en not specified\n",
|
||||
__func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "samsung,pmic_haptic_pwr_en", &vibrator_drvdata.is_pmic_haptic_pwr_en);
|
||||
if (rc) {
|
||||
pr_err("%s:%d, is_pmic_haptic_pwr_en not specified\n",
|
||||
__func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
//vibrator_drvdata.is_pmic_vib_pwm = 0; AP PWM PIN
|
||||
//vibrator_drvdata.is_pmic_vib_pwm = 1; PMIC PWM PIN
|
||||
rc = of_property_read_u32(np, "samsung,is_pmic_vib_pwm", &vibrator_drvdata.is_pmic_vib_pwm);
|
||||
if (rc) {
|
||||
pr_err("%s:%d, is_pmic_vib_pwm not specified\n",
|
||||
__func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(np, "samsung,pwm_period_us", &vibrator_drvdata.pwm_period_us);
|
||||
if (rc) {
|
||||
pr_err("%s:%d, pwm_period_us not specified\n",
|
||||
__func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
rc = of_property_read_u32(np, "samsung,duty_us", &vibrator_drvdata.duty_us);
|
||||
if (rc) {
|
||||
pr_err("%s:%d, duty_us not specified\n",
|
||||
__func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
rc = of_property_read_u32(np, "samsung,changed_chip", &vibrator_drvdata.changed_chip);
|
||||
if (rc) {
|
||||
pr_info("%s:%d, changed_chip not specified\n", __func__, __LINE__);
|
||||
vibrator_drvdata.changed_chip = 0;
|
||||
rc = 0;
|
||||
} else {
|
||||
if (vibrator_drvdata.changed_chip)
|
||||
vibrator_drvdata.changed_en_gpio = of_get_named_gpio(np, "samsung,changed_en_gpio", 0);
|
||||
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MOTOR_DRV_MAX77804K) || defined(CONFIG_MOTOR_DRV_MAX77828)
|
||||
static void max77803_haptic_power_onoff(int onoff)
|
||||
{
|
||||
int ret;
|
||||
static struct regulator *reg_l23;
|
||||
|
||||
if (!reg_l23) {
|
||||
reg_l23 = regulator_get(NULL, "8084_l23");
|
||||
ret = regulator_set_voltage(reg_l23, 3000000, 3000000);
|
||||
if (IS_ERR(reg_l23)) {
|
||||
printk(KERN_ERR"could not get 8084_l23, rc = %ld\n",
|
||||
PTR_ERR(reg_l23));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (onoff) {
|
||||
ret = regulator_enable(reg_l23);
|
||||
if (ret) {
|
||||
printk(KERN_ERR"enable l23 failed, rc=%d\n", ret);
|
||||
return;
|
||||
}
|
||||
printk(KERN_DEBUG"haptic power_on is finished.\n");
|
||||
} else {
|
||||
if (regulator_is_enabled(reg_l23)) {
|
||||
ret = regulator_disable(reg_l23);
|
||||
if (ret) {
|
||||
printk(KERN_ERR"disable l23 failed, rc=%d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
printk(KERN_DEBUG"haptic power_off is finished.\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(CONFIG_MOTOR_DRV_MAX77803)
|
||||
static void max77803_haptic_power_onoff(int onoff)
|
||||
{
|
||||
int ret;
|
||||
#if defined(CONFIG_SEC_H_PROJECT) || defined(CONFIG_SEC_MONTBLANC_PROJECT) || defined(CONFIG_SEC_JS_PROJECT) || \
|
||||
defined(CONFIG_MACH_FLTEEUR) || defined(CONFIG_MACH_FLTESKT) || defined(CONFIG_MACH_JVELTEEUR) ||\
|
||||
defined(CONFIG_MACH_VIKALCU) || defined(CONFIG_SEC_LOCALE_KOR_FRESCO)
|
||||
static struct regulator *reg_l23;
|
||||
|
||||
if (!reg_l23) {
|
||||
reg_l23 = regulator_get(NULL, "8941_l23");
|
||||
#if defined(CONFIG_MACH_FLTESKT)
|
||||
ret = regulator_set_voltage(reg_l23, 3000000, 3000000);
|
||||
#elif defined(CONFIG_MACH_HLTEVZW)
|
||||
ret = regulator_set_voltage(reg_l23, 3100000, 3100000);
|
||||
#elif defined(CONFIG_SEC_LOCALE_KOR_FRESCO)
|
||||
ret = regulator_set_voltage(reg_l23, 2488000,2488000);
|
||||
#else
|
||||
ret = regulator_set_voltage(reg_l23, 2825000, 2825000);
|
||||
#endif
|
||||
if (IS_ERR(reg_l23)) {
|
||||
printk(KERN_ERR"could not get 8941_l23, rc = %ld\n",
|
||||
PTR_ERR(reg_l23));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (onoff) {
|
||||
ret = regulator_enable(reg_l23);
|
||||
if (ret) {
|
||||
printk(KERN_ERR"enable l23 failed, rc=%d\n", ret);
|
||||
return;
|
||||
}
|
||||
printk(KERN_DEBUG"haptic power_on is finished.\n");
|
||||
} else {
|
||||
if (regulator_is_enabled(reg_l23)) {
|
||||
ret = regulator_disable(reg_l23);
|
||||
if (ret) {
|
||||
printk(KERN_ERR"disable l23 failed, rc=%d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
printk(KERN_DEBUG"haptic power_off is finished.\n");
|
||||
}
|
||||
#else
|
||||
static struct regulator *reg_l17;
|
||||
|
||||
if (!reg_l17) {
|
||||
reg_l17 = regulator_get(NULL, "8941_l17");
|
||||
ret = regulator_set_voltage(reg_l17, 3000000, 3000000);
|
||||
|
||||
if (IS_ERR(reg_l17)) {
|
||||
printk(KERN_ERR"could not get 8941_l17, rc = %ld\n",
|
||||
PTR_ERR(reg_l17));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (onoff) {
|
||||
ret = regulator_enable(reg_l17);
|
||||
if (ret) {
|
||||
printk(KERN_ERR"enable l17 failed, rc=%d\n", ret);
|
||||
return;
|
||||
}
|
||||
printk(KERN_DEBUG"haptic power_on is finished.\n");
|
||||
} else {
|
||||
if (regulator_is_enabled(reg_l17)) {
|
||||
ret = regulator_disable(reg_l17);
|
||||
if (ret) {
|
||||
printk(KERN_ERR"disable l17 failed, rc=%d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
printk(KERN_DEBUG"haptic power_off is finished.\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(CONFIG_MOTOR_DRV_DRV2603)
|
||||
void drv2603_gpio_en(bool en)
|
||||
{
|
||||
if (en) {
|
||||
gpio_direction_output(vibrator_drvdata.drv2603_en_gpio, 1);
|
||||
} else {
|
||||
gpio_direction_output(vibrator_drvdata.drv2603_en_gpio, 0);
|
||||
}
|
||||
}
|
||||
static int32_t drv2603_gpio_init(void)
|
||||
{
|
||||
int ret;
|
||||
ret = gpio_request(vibrator_drvdata.drv2603_en_gpio, "vib enable");
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "vib enable gpio_request is failed\n");
|
||||
return 1;
|
||||
}
|
||||
gpio_direction_output(vibrator_drvdata.drv2603_en_gpio, 0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#if defined(CONFIG_MOTOR_DRV_MAX77888)
|
||||
void max77888_gpio_en(bool en)
|
||||
{
|
||||
if (en) {
|
||||
gpio_direction_output(vibrator_drvdata.max77888_en_gpio, 1);
|
||||
} else {
|
||||
gpio_direction_output(vibrator_drvdata.max77888_en_gpio, 0);
|
||||
}
|
||||
}
|
||||
static int32_t max77888_gpio_init(void)
|
||||
{
|
||||
int ret;
|
||||
ret = gpio_request(vibrator_drvdata.max77888_en_gpio, "vib enable");
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "vib enable gpio_request is failed\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void vibrator_initialize(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* set gpio config */
|
||||
if (vibrator_drvdata.is_pmic_vib_pwm) { //PMIC PWM
|
||||
ret = gpio_request(vibrator_drvdata.vib_pwm_gpio, \
|
||||
"vib pwm");
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR"vib pwm gpio_request is failed\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
ret = pm8xxx_gpio_config(vibrator_drvdata.vib_pwm_gpio,\
|
||||
&vib_pwm);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "failed to configure vib pwm pmic gpio\n");
|
||||
goto err2;
|
||||
}
|
||||
} else { //AP PWM
|
||||
#if defined(CONFIG_MACH_S3VE3G_EUR)
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,
|
||||
0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),
|
||||
GPIO_CFG_ENABLE);
|
||||
#else
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.vib_pwm_gpio,
|
||||
0, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
|
||||
GPIO_CFG_ENABLE);
|
||||
#endif
|
||||
}
|
||||
#if defined(CONFIG_MOTOR_ISA1000)
|
||||
if (!vibrator_drvdata.is_pmic_vib_en) {
|
||||
ret = gpio_request(vibrator_drvdata.vib_en_gpio,"vib enable");
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "vib enable gpio_request is failed\n");
|
||||
goto err2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MOTOR_DRV_DRV2603)
|
||||
if (drv2603_gpio_init())
|
||||
goto err2;
|
||||
#elif defined(CONFIG_MOTOR_DRV_MAX77888)
|
||||
if(max77888_gpio_init())
|
||||
goto err2;
|
||||
#endif
|
||||
|
||||
return;
|
||||
err2:
|
||||
printk(KERN_ERR "%s failed check.\n", __func__);
|
||||
}
|
||||
|
||||
static struct device *vib_dev;
|
||||
extern struct class *sec_class;
|
||||
|
||||
static ssize_t show_vib_tuning(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
sprintf(buf, "gp_m %d, gp_n %d, gp_d %d, pwm_mul %d, strength %d, min_str %d\n",
|
||||
g_nlra_gp_clk_m, g_nlra_gp_clk_n, g_nlra_gp_clk_d,
|
||||
g_nlra_gp_clk_pwm_mul, motor_strength, motor_min_strength);
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
static ssize_t store_vib_tuning(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
int retval;
|
||||
int temp_m, temp_n, temp_str;
|
||||
|
||||
retval = sscanf(buf, "%d %d %d", &temp_m, &temp_n, &temp_str);
|
||||
if (retval == 0) {
|
||||
pr_info("[VIB]: %s, fail to get vib_tuning value\n", __func__);
|
||||
return count;
|
||||
}
|
||||
|
||||
g_nlra_gp_clk_m = temp_m;
|
||||
g_nlra_gp_clk_n = temp_n;
|
||||
g_nlra_gp_clk_d = temp_n / 2;
|
||||
g_nlra_gp_clk_pwm_mul = temp_n;
|
||||
motor_strength = temp_str;
|
||||
motor_min_strength = g_nlra_gp_clk_n*MOTOR_MIN_STRENGTH/100;
|
||||
|
||||
pr_info("[VIB]: %s gp_m %d, gp_n %d, gp_d %d, pwm_mul %d, strength %d, min_str %d\n", __func__,
|
||||
g_nlra_gp_clk_m, g_nlra_gp_clk_n, g_nlra_gp_clk_d,
|
||||
g_nlra_gp_clk_pwm_mul, motor_strength, motor_min_strength);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(vib_tuning, 0660, show_vib_tuning, store_vib_tuning);
|
||||
|
||||
static ssize_t intensity_store(struct device *dev,
|
||||
struct device_attribute *devattr, const char *buf, size_t count)
|
||||
{
|
||||
struct timed_output_dev *t_dev = dev_get_drvdata(dev);
|
||||
struct ss_vib *vib = container_of(t_dev, struct ss_vib, timed_dev);
|
||||
int ret = 0, set_intensity = 0;
|
||||
|
||||
ret = kstrtoint(buf, 0, &set_intensity);
|
||||
|
||||
if ((set_intensity < 0) || (set_intensity > MAX_INTENSITY)) {
|
||||
pr_err("[VIB]: %sout of rage\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vibe_set_intensity(set_intensity);
|
||||
vib->intensity = set_intensity;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t intensity_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct timed_output_dev *t_dev = dev_get_drvdata(dev);
|
||||
struct ss_vib *vib = container_of(t_dev, struct ss_vib, timed_dev);
|
||||
|
||||
return sprintf(buf, "intensity: %u\n", vib->intensity);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(intensity, 0660, intensity_show, intensity_store);
|
||||
|
||||
static int ss_vibrator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ss_vib *vib;
|
||||
int rc = 0;
|
||||
|
||||
pr_info("[VIB]: %s\n",__func__);
|
||||
|
||||
motor_min_strength = g_nlra_gp_clk_n*MOTOR_MIN_STRENGTH/100;
|
||||
vib = devm_kzalloc(&pdev->dev, sizeof(*vib), GFP_KERNEL);
|
||||
if (!vib) {
|
||||
pr_err("[VIB]: %s : Failed to allocate memory\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!pdev->dev.of_node) {
|
||||
pr_err("[VIB]: %s failed, DT is NULL", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
vib->dev = &pdev->dev;
|
||||
rc = vibrator_parse_dt(vib);
|
||||
if(rc)
|
||||
return rc;
|
||||
|
||||
#if defined(CONFIG_MACH_HLTEDCM) || defined(CONFIG_MACH_HLTEKDI) || defined(CONFIG_MACH_JS01LTEDCM)
|
||||
virt_mmss_gp1_base = ioremap(MSM_MMSS_GP3_BASE,0x28);
|
||||
#elif defined(CONFIG_SEC_BERLUTI_PROJECT) || defined(CONFIG_MACH_S3VE3G_EUR)
|
||||
virt_mmss_gp1_base = ioremap(MSM_MMSS_GP0_BASE,0x28);
|
||||
#else
|
||||
virt_mmss_gp1_base = ioremap(MSM_MMSS_GP1_BASE,0x28);
|
||||
#endif
|
||||
if (!virt_mmss_gp1_base)
|
||||
panic("[VIB]: Unable to ioremap MSM_MMSS_GP1 memory!");
|
||||
|
||||
#if defined(CONFIG_MOTOR_DRV_MAX77803) || defined(CONFIG_MOTOR_DRV_MAX77804K) || defined(CONFIG_MOTOR_DRV_MAX77828)
|
||||
vibrator_drvdata.power_onoff = max77803_haptic_power_onoff;
|
||||
#else
|
||||
vibrator_drvdata.power_onoff = NULL;
|
||||
#endif
|
||||
vibrator_drvdata.pwm_dev = NULL;
|
||||
|
||||
vib->state = 0;
|
||||
vib->intensity = MAX_INTENSITY;
|
||||
vib->timeout = VIB_DEFAULT_TIMEOUT;
|
||||
|
||||
vibrator_initialize();
|
||||
vibe_set_intensity(vib->intensity);
|
||||
INIT_WORK(&vib->work, ss_vibrator_update);
|
||||
mutex_init(&vib->lock);
|
||||
|
||||
vib->queue = create_singlethread_workqueue("ss_vibrator");
|
||||
if (!vib->queue) {
|
||||
pr_err("[VIB]: %s: can't create workqueue\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hrtimer_init(&vib->vib_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
vib->vib_timer.function = vibrator_timer_func;
|
||||
|
||||
vib->timed_dev.name = "vibrator";
|
||||
vib->timed_dev.get_time = vibrator_get_time;
|
||||
vib->timed_dev.enable = vibrator_enable;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, vib);
|
||||
|
||||
rc = timed_output_dev_register(&vib->timed_dev);
|
||||
if (rc < 0) {
|
||||
pr_err("[VIB]: timed_output_dev_register fail (rc=%d)\n", rc);
|
||||
goto err_read_vib;
|
||||
}
|
||||
|
||||
rc = sysfs_create_file(&vib->timed_dev.dev->kobj, &dev_attr_intensity.attr);
|
||||
if (rc < 0) {
|
||||
pr_err("[VIB]: Failed to register sysfs intensity: %d\n", rc);
|
||||
}
|
||||
|
||||
vib_dev = device_create(sec_class, NULL, 0, NULL, "vib");
|
||||
if (IS_ERR(vib_dev)) {
|
||||
pr_info("[VIB]: Failed to create device for samsung vib\n");
|
||||
}
|
||||
|
||||
rc = sysfs_create_file(&vib_dev->kobj, &dev_attr_vib_tuning.attr);
|
||||
if (rc) {
|
||||
pr_info("Failed to create sysfs group for samsung specific led\n");
|
||||
}
|
||||
|
||||
wake_lock_init(&vib_wake_lock, WAKE_LOCK_SUSPEND, "vib_preset");
|
||||
pm_qos_add_request(&pm_qos_req, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
|
||||
|
||||
return 0;
|
||||
err_read_vib:
|
||||
iounmap(virt_mmss_gp1_base);
|
||||
destroy_workqueue(vib->queue);
|
||||
mutex_destroy(&vib->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ss_vibrator_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ss_vib *vib = dev_get_drvdata(&pdev->dev);
|
||||
iounmap(virt_mmss_gp1_base);
|
||||
pm_qos_remove_request(&pm_qos_req);
|
||||
|
||||
destroy_workqueue(vib->queue);
|
||||
mutex_destroy(&vib->lock);
|
||||
wake_lock_destroy(&vib_wake_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id vib_motor_match[] = {
|
||||
{ .compatible = "vibrator",
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver ss_vibrator_platdrv =
|
||||
{
|
||||
.driver =
|
||||
{
|
||||
.name = "vibrator",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = vib_motor_match,
|
||||
.pm = &vibrator_pm_ops,
|
||||
},
|
||||
.probe = ss_vibrator_probe,
|
||||
.remove = ss_vibrator_remove,
|
||||
};
|
||||
|
||||
static int __init ss_timed_vibrator_init(void)
|
||||
{
|
||||
return platform_driver_register(&ss_vibrator_platdrv);
|
||||
}
|
||||
|
||||
void __exit ss_timed_vibrator_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ss_vibrator_platdrv);
|
||||
}
|
||||
module_init(ss_timed_vibrator_init);
|
||||
module_exit(ss_timed_vibrator_exit);
|
||||
|
||||
MODULE_AUTHOR("Samsung Corporation");
|
||||
MODULE_DESCRIPTION("timed output vibrator device");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -1,100 +1,39 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** tspdrv.h
|
||||
**
|
||||
** Description:
|
||||
** Constants and type definitions for the TouchSense Kernel Module.
|
||||
**
|
||||
** Portions Copyright (c) 2008-2010 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
|
||||
** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** =========================================================================
|
||||
*/
|
||||
|
||||
#ifndef _TSPDRV_H
|
||||
#define _TSPDRV_H
|
||||
#define VIBE_DEBUG
|
||||
#include <mach/msm_iomap.h>
|
||||
/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only 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/mfd/pm8xxx/pm8921.h>
|
||||
|
||||
#define PM8921_GPIO_BASE NR_GPIO_IRQS
|
||||
#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE)
|
||||
#ifndef _VIBRATOR_H
|
||||
#define _VIBRATOR_H
|
||||
|
||||
extern struct vibrator_platform_data vibrator_drvdata;
|
||||
|
||||
/* Constants */
|
||||
#define MODULE_NAME "tspdrv"
|
||||
#define TSPDRV "/dev/"MODULE_NAME
|
||||
#define TSPDRV_MAGIC_NUMBER 0x494D4D52
|
||||
|
||||
#if defined(CONFIG_TACTILE_ASSIST) || defined(CONFIG_VIBRATOR_UPDATE)
|
||||
#define TSPDRV_IOCTL_GROUP 0x52
|
||||
#define TSPDRV_SET_MAGIC_NUMBER _IO(TSPDRV_IOCTL_GROUP, 2)
|
||||
#endif
|
||||
|
||||
#define TSPDRV_STOP_KERNEL_TIMER _IO(TSPDRV_MAGIC_NUMBER & 0xFF, 1)
|
||||
|
||||
/*
|
||||
** Obsolete IOCTL command
|
||||
** #define TSPDRV_IDENTIFY_CALLER _IO(TSPDRV_MAGIC_NUMBER & 0xFF, 2)
|
||||
*/
|
||||
#define TSPDRV_ENABLE_AMP _IO(TSPDRV_MAGIC_NUMBER & 0xFF, 3)
|
||||
#define TSPDRV_DISABLE_AMP _IO(TSPDRV_MAGIC_NUMBER & 0xFF, 4)
|
||||
#define TSPDRV_GET_NUM_ACTUATORS _IO(TSPDRV_MAGIC_NUMBER & 0xFF, 5)
|
||||
#define VIBE_MAX_DEVICE_NAME_LENGTH 64
|
||||
#define SPI_HEADER_SIZE 3 /* DO NOT CHANGE - SPI buffer header size */
|
||||
/* DO NOT CHANGE - maximum number of samples */
|
||||
#define VIBE_OUTPUT_SAMPLE_SIZE 50
|
||||
|
||||
/* Type definitions */
|
||||
#ifdef __KERNEL__
|
||||
typedef struct {
|
||||
u_int8_t nactuator_index; /* 1st byte is actuator index */
|
||||
u_int8_t nbit_depth; /* 2nd byte is bit depth */
|
||||
u_int8_t nbuffer_size; /* 3rd byte is data size */
|
||||
u_int8_t data_buffer[VIBE_OUTPUT_SAMPLE_SIZE];
|
||||
} samples_buffer;
|
||||
|
||||
typedef struct {
|
||||
int8_t nindex_playing_buffer;
|
||||
u_int8_t nindex_output_value;
|
||||
/* Use 2 buffers to receive samples from user mode */
|
||||
samples_buffer actuator_samples[2];
|
||||
} actuator_samples_buffer;
|
||||
|
||||
#endif
|
||||
struct pm_gpio vib_pwm = {
|
||||
.direction = PM_GPIO_DIR_OUT,
|
||||
.output_buffer = 0,
|
||||
.output_value = 0,
|
||||
.pull = PM_GPIO_PULL_NO,
|
||||
.vin_sel = 0,
|
||||
.out_strength = PM_GPIO_STRENGTH_HIGH,
|
||||
.function = PM_GPIO_FUNC_1,
|
||||
.inv_int_pol = 0,
|
||||
};
|
||||
|
||||
/* Error and Return value codes */
|
||||
#define VIBE_S_SUCCESS 0 /* Success */
|
||||
#define VIBE_E_FAIL -4 /* Generic error */
|
||||
|
||||
#if defined(VIBE_RECORD) && defined(VIBE_DEBUG)
|
||||
void _RecorderInit(void);
|
||||
void _RecorderTerminate(void);
|
||||
void _RecorderReset(int nActuator);
|
||||
void _Record(int actuatorIndex, const char *format, ...);
|
||||
#endif
|
||||
#define VIBRATION_ON 1
|
||||
#define VIBRATION_OFF 0
|
||||
|
||||
int32_t g_nforce_32;
|
||||
#define VIBRATION_SUCCESS 0 /* Success */
|
||||
#define VIBRATION_FAIL -1 /* Generic error */
|
||||
#define VIBRATION_ON 1
|
||||
#define VIBRATION_OFF 0
|
||||
|
||||
#define MAX_INTENSITY 10000
|
||||
|
||||
#if defined(CONFIG_MACH_KS01SKT) \
|
||||
|| defined(CONFIG_MACH_KS01KTT) || defined(CONFIG_MACH_KS01LGT) \
|
||||
|
@ -109,7 +48,7 @@ int32_t g_nforce_32;
|
|||
#elif defined(CONFIG_MACH_HLTEUSC) || defined(CONFIG_MACH_HLTEVZW)
|
||||
#define MOTOR_STRENGTH 99/*MOTOR_STRENGTH 99 %*/
|
||||
#elif defined(CONFIG_MACH_KACTIVELTE_KOR)
|
||||
#define MOTOR_STRENGTH 93/*MOTOR_STRENGTH 93 %*/
|
||||
#define MOTOR_STRENGTH 95/*MOTOR_STRENGTH 95 %*/
|
||||
#elif defined(CONFIG_SEC_K_PROJECT) || defined(CONFIG_SEC_KACTIVE_PROJECT) || defined(CONFIG_SEC_KSPORTS_PROJECT) \
|
||||
|| defined(CONFIG_SEC_PATEK_PROJECT)
|
||||
#define MOTOR_STRENGTH 98/*MOTOR_STRENGTH 98 %*/
|
||||
|
@ -182,6 +121,8 @@ int32_t g_nforce_32;
|
|||
|
||||
|
||||
#define MOTOR_MIN_STRENGTH 54/*IMMERSION VALUE*/
|
||||
|
||||
#define PM_QOS_NONIDLE_VALUE 300
|
||||
/*
|
||||
* ** Global variables for LRA PWM M,N and D values.
|
||||
* */
|
||||
|
@ -204,7 +145,6 @@ int32_t motor_min_strength;
|
|||
|
||||
#define __outpdw(port, val) iowrite32(val, port)
|
||||
|
||||
|
||||
#define in_dword(addr) (__inpdw(addr))
|
||||
#define in_dword_masked(addr, mask) (__inpdw(addr) & (mask))
|
||||
#define out_dword(addr, val) __outpdw(addr, val)
|
||||
|
@ -296,28 +236,9 @@ static void __iomem *virt_mmss_gp1_base;
|
|||
#define __msmhwio_outm(hwiosym, mask, val) HWIO_##hwiosym##_OUTM(mask, val)
|
||||
#define HWIO_OUTM(hwiosym, mask, val) __msmhwio_outm(hwiosym, mask, val)
|
||||
int32_t vibe_pwm_onoff(u8 onoff);
|
||||
int32_t vibe_set_pwm_freq(int nForce);
|
||||
int32_t vibe_set_pwm_freq(int intensity);
|
||||
int vib_config_pwm_device(void);
|
||||
#endif
|
||||
/* Kernel Debug Macros */
|
||||
#ifdef __KERNEL__
|
||||
#ifdef VIBE_DEBUG
|
||||
#define DbgOut(_x_, ...) printk(_x_, ##__VA_ARGS__)
|
||||
#else /* VIBE_DEBUG */
|
||||
#define DbgOut(_x_)
|
||||
#endif /* VIBE_DEBUG */
|
||||
|
||||
#if defined(VIBE_RECORD) && defined(VIBE_DEBUG)
|
||||
#define DbgRecorderInit(_x_) _RecorderInit _x_
|
||||
#define DbgRecorderTerminate(_x_) _RecorderTerminate _x_
|
||||
#define DbgRecorderReset(_x_) _RecorderReset _x_
|
||||
#define DbgRecord(_x_) _Record _x_
|
||||
#else /* defined(VIBE_RECORD) && defined(VIBE_DEBUG) */
|
||||
#define DbgRecorderInit(_x_)
|
||||
#define DbgRecorderTerminate(_x_)
|
||||
#define DbgRecorderReset(_x_)
|
||||
#define DbgRecord(_x_)
|
||||
#endif /* defined(VIBE_RECORD) && defined(VIBE_DEBUG) */
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#if defined(CONFIG_MOTOR_DRV_MAX77803)
|
||||
extern void max77803_vibtonz_en(bool en);
|
||||
|
@ -334,4 +255,4 @@ void drv2603_gpio_en(bool);
|
|||
static int32_t drv2603_gpio_init(void);
|
||||
#endif
|
||||
|
||||
#endif /* _TSPDRV_H */
|
||||
#endif /* _VIBRATOR_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** tspdrvOutputDataHandler_drv2604.c
|
||||
**
|
||||
** Description:
|
||||
** Output data handler module for TSP 3000 Edition.
|
||||
** ‘This module supports only one actuator and one sample per update.
|
||||
**
|
||||
** Portions Copyright (c) 2011-2013 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
|
||||
** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** =========================================================================
|
||||
*/
|
||||
|
||||
VibeUInt8 g_nOutputData = 0;
|
||||
VibeBool g_bOutputDataBufferEmpty = 1;
|
||||
|
||||
/* TSP 3000 devices support only one actuator and one output per update */
|
||||
#define SPI_BUFFER_SIZE (1 + SPI_HEADER_SIZE)
|
||||
|
||||
int SaveOutputData(const char *outputDataBuffer, int count)
|
||||
{
|
||||
/* Check buffer size */
|
||||
if (count < SPI_HEADER_SIZE)
|
||||
{
|
||||
DbgOut((DBL_ERROR, "SaveOutputData: invalid write buffer size.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check data buffer address */
|
||||
if (0 == outputDataBuffer)
|
||||
{
|
||||
DbgOut((DBL_ERROR, "SaveOutputData: outputDataBuffer invalid.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count == SPI_HEADER_SIZE)
|
||||
{
|
||||
/*
|
||||
** An "empty" packet (header with data size equal to 0)
|
||||
** is used by the user mode daemon to start blocking-timer process,
|
||||
** even if no output force data is sent.
|
||||
*/
|
||||
g_bOutputDataBufferEmpty = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_nOutputData = outputDataBuffer[SPI_HEADER_SIZE];
|
||||
g_bOutputDataBufferEmpty = 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool SendOutputData(void)
|
||||
{
|
||||
if (g_bOutputDataBufferEmpty)
|
||||
{
|
||||
#ifdef VIBE_RUNTIME_RECORD
|
||||
if (atomic_read(&g_bRuntimeRecord)) {
|
||||
DbgRecord((0,"0\n"));
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
ImmVibeSPI_ForceOut_SetSamples(0, 8, 1, &g_nOutputData);
|
||||
|
||||
/* Reset the buffer */
|
||||
g_nOutputData = 0;
|
||||
g_bOutputDataBufferEmpty = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ResetOutputData(void)
|
||||
{
|
||||
g_nOutputData = 0;
|
||||
g_bOutputDataBufferEmpty = 1;
|
||||
}
|
||||
|
||||
bool IsOutputDataBufferEmpty(void)
|
||||
{
|
||||
return g_bOutputDataBufferEmpty;
|
||||
}
|
|
@ -1,706 +0,0 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** tspdrv_drv2604.c
|
||||
**
|
||||
** Description:
|
||||
** TouchSense Kernel Module main entry-point.
|
||||
**
|
||||
** Portions Copyright (c) 2008-2014 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
|
||||
** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** =========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#define __KERNEL__
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <tspdrv_drv2604.h>
|
||||
#include <linux/vibrator.h>
|
||||
#include "../staging/android/timed_output.h"
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/wakelock.h>
|
||||
|
||||
static int g_nTimerPeriodMs = 5; /* 5ms timer by default. This variable could be used by the SPI.*/
|
||||
|
||||
#ifdef VIBE_RUNTIME_RECORD
|
||||
/* Flag indicating whether runtime recorder on or off */
|
||||
static atomic_t g_bRuntimeRecord;
|
||||
#endif
|
||||
|
||||
#include <Immvibespi_drv2604.c>
|
||||
#if (defined(VIBE_DEBUG) && defined(VIBE_RECORD)) || defined(VIBE_RUNTIME_RECORD)
|
||||
#include <tspdrvRecorder.c>
|
||||
#endif
|
||||
|
||||
/* Device name and version information */
|
||||
#define VERSION_STR " v3.7.45.1\n" /* DO NOT CHANGE - this is auto-generated */
|
||||
#define VERSION_STR_LEN 16 /* account extra space for future extra digits in version number */
|
||||
static char g_szDeviceName[ (VIBE_MAX_DEVICE_NAME_LENGTH
|
||||
+ VERSION_STR_LEN)
|
||||
* NUM_ACTUATORS]; /* initialized in init_module */
|
||||
static size_t g_cchDeviceName; /* initialized in init_module */
|
||||
|
||||
/* Flag indicating whether the driver is in use */
|
||||
static char g_bIsPlaying = false;
|
||||
|
||||
/* Flag indicating the debug level*/
|
||||
static atomic_t g_nDebugLevel;
|
||||
|
||||
|
||||
/* Buffer to store data sent to SPI */
|
||||
#define MAX_SPI_BUFFER_SIZE (NUM_ACTUATORS * (VIBE_OUTPUT_SAMPLE_SIZE + SPI_HEADER_SIZE))
|
||||
|
||||
static char g_cWriteBuffer[MAX_SPI_BUFFER_SIZE];
|
||||
|
||||
|
||||
#if ((LINUX_VERSION_CODE & 0xFFFF00) < KERNEL_VERSION(2,6,0))
|
||||
#error Unsupported Kernel version
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_UNLOCKED_IOCTL
|
||||
#define HAVE_UNLOCKED_IOCTL 0
|
||||
#endif
|
||||
|
||||
#ifdef IMPLEMENT_AS_CHAR_DRIVER
|
||||
static int g_nMajor = 0;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Needs to be included after the global variables because they use them */
|
||||
#include <tspdrvOutputDataHandler_drv2604.c>
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
#include <VibeOSKernelLinuxHRTime_drv2604.c>
|
||||
#else
|
||||
#include <VibeOSKernelLinuxTime_drv2604.c>
|
||||
#endif
|
||||
|
||||
static struct hrtimer timer;
|
||||
static int max_timeout = 10000;
|
||||
static int vibrator_value = 0;
|
||||
static int vibrator_work;
|
||||
static int drv2604_enabled;
|
||||
|
||||
static void _set_vibetonz_work(struct work_struct *unused);
|
||||
|
||||
static DECLARE_WORK(vibetonz_work, _set_vibetonz_work);
|
||||
struct vibrator_platform_data_drv2604 vibrator_drvdata;
|
||||
|
||||
static int set_vibetonz(int timeout)
|
||||
{
|
||||
int8_t strength;
|
||||
if(!timeout) {
|
||||
strength = 0;
|
||||
ImmVibeSPI_ForceOut_SetSamples(0,8,1,&strength);
|
||||
}
|
||||
else {
|
||||
DbgOut((KERN_INFO "tspdrv: ENABLE\n"));
|
||||
strength = 126;
|
||||
ImmVibeSPI_ForceOut_SetSamples(0,8,1,&strength);
|
||||
}
|
||||
|
||||
vibrator_value = timeout;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _set_vibetonz_work(struct work_struct *unused)
|
||||
{
|
||||
set_vibetonz(vibrator_work);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static enum hrtimer_restart vibetonz_timer_func(struct hrtimer *timer)
|
||||
{
|
||||
vibrator_work = 0;
|
||||
schedule_work(&vibetonz_work);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static int get_time_for_vibetonz(struct timed_output_dev *dev)
|
||||
{
|
||||
int remaining;
|
||||
|
||||
if (hrtimer_active(&timer)) {
|
||||
ktime_t r = hrtimer_get_remaining(&timer);
|
||||
remaining = (int)ktime_to_ms(r);
|
||||
} else
|
||||
remaining = 0;
|
||||
|
||||
if (vibrator_value ==-1)
|
||||
remaining = -1;
|
||||
|
||||
return remaining;
|
||||
|
||||
}
|
||||
|
||||
static void enable_vibetonz_from_user(struct timed_output_dev *dev,int value)
|
||||
{
|
||||
printk("[VIBETONZ] %s : time = %d msec \n",__func__,value);
|
||||
hrtimer_cancel(&timer);
|
||||
/* set_vibetonz(value); */
|
||||
vibrator_work = value;
|
||||
schedule_work(&vibetonz_work);
|
||||
|
||||
|
||||
if (value > 0)
|
||||
{
|
||||
if (value > max_timeout)
|
||||
value = max_timeout;
|
||||
|
||||
hrtimer_start(&timer,ktime_set(value / 1000, (value % 1000) * 1000000),
|
||||
HRTIMER_MODE_REL);
|
||||
vibrator_value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct timed_output_dev timed_output_vt = {
|
||||
.name = "vibrator",
|
||||
.get_time = get_time_for_vibetonz,
|
||||
.enable = enable_vibetonz_from_user,
|
||||
};
|
||||
|
||||
static void vibetonz_start(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
timer.function = vibetonz_timer_func;
|
||||
|
||||
ret = timed_output_dev_register(&timed_output_vt);
|
||||
if(ret)
|
||||
DbgOut((KERN_ERR "[VIBETONZ] timed_output_dev_register is fail \n"));
|
||||
}
|
||||
|
||||
|
||||
/* File IO */
|
||||
static int open(struct inode *inode, struct file *file);
|
||||
static int release(struct inode *inode, struct file *file);
|
||||
static ssize_t read(struct file *file, char *buf, size_t count, loff_t *ppos);
|
||||
static ssize_t write(struct file *file, const char *buf, size_t count, loff_t *ppos);
|
||||
#if HAVE_UNLOCKED_IOCTL
|
||||
static long unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
#else
|
||||
static int ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
|
||||
#endif
|
||||
static struct file_operations fops =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
.read = read,
|
||||
.write = write,
|
||||
#if HAVE_UNLOCKED_IOCTL
|
||||
.unlocked_ioctl = unlocked_ioctl,
|
||||
#else
|
||||
.ioctl = ioctl,
|
||||
#endif
|
||||
.open = open,
|
||||
.release = release,
|
||||
.llseek = default_llseek /* using default implementation as declared in linux/fs.h */
|
||||
};
|
||||
|
||||
#ifndef IMPLEMENT_AS_CHAR_DRIVER
|
||||
static struct miscdevice miscdev =
|
||||
{
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = MODULE_NAME,
|
||||
.fops = &fops
|
||||
};
|
||||
#endif
|
||||
|
||||
static int drv2604_parse_dt(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
vibrator_drvdata.motor_en = of_get_named_gpio(np, "drv2604,motor_en",0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int __devinit drv2604_vibrator_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
||||
int nRet, i,error; /* initialized below */
|
||||
char status;
|
||||
#if SKIP_AUTOCAL == 0
|
||||
int nCalibrationCount = 0;
|
||||
#endif
|
||||
DbgOut((KERN_INFO "tspdrv: Probe called.\n"));
|
||||
|
||||
vibrator_drvdata.client = client;
|
||||
if(!(client->dev.of_node)){
|
||||
DbgOut(KERN_ERR "tspdrv: tspdrv probe failed, DT is NULL");
|
||||
return -ENODEV;
|
||||
}
|
||||
error = drv2604_parse_dt(&client->dev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if( gpio_request(vibrator_drvdata.motor_en, "MOTOR_EN") < 0)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpio_direction_output(vibrator_drvdata.motor_en, 0);
|
||||
gpio_export(vibrator_drvdata.motor_en, 0);
|
||||
|
||||
|
||||
nRet = misc_register(&miscdev);
|
||||
if (nRet)
|
||||
{
|
||||
DbgOut((KERN_ERR "tspdrv: misc_register failed.\n"));
|
||||
return nRet;
|
||||
}
|
||||
|
||||
#if USE_DRV2604_EN_PIN
|
||||
drv2604_set_en(true);
|
||||
#endif
|
||||
#if USE_DRV2604_STANDBY
|
||||
/* Wait 1000 us for chip power to stabilize */
|
||||
usleep_range(100000, 100000);
|
||||
drv2604_change_mode(MODE_DEVICE_READY);
|
||||
#endif
|
||||
/* Wait 1000 us for chip power to stabilize */
|
||||
usleep_range(1000, 1000);
|
||||
|
||||
#if SKIP_AUTOCAL
|
||||
usleep_range(100000, 100000);
|
||||
drv2604_write_reg_val(init_sequence, sizeof(init_sequence));
|
||||
status = drv2604_read_reg(STATUS_REG);
|
||||
#else
|
||||
/* Run auto-calibration */
|
||||
do{
|
||||
drv2604_write_reg_val(autocal_sequence, sizeof(autocal_sequence));
|
||||
|
||||
/* Wait until the procedure is done */
|
||||
drv2604_poll_go_bit();
|
||||
|
||||
/* Read status */
|
||||
status = drv2604_read_reg(STATUS_REG);
|
||||
nCalibrationCount++;
|
||||
|
||||
} while (((status & DIAG_RESULT_MASK) == AUTO_CAL_FAILED) && (nCalibrationCount < MAX_AUTOCALIBRATION_ATTEMPT));
|
||||
|
||||
/* Check result */
|
||||
if ((status & DIAG_RESULT_MASK) == AUTO_CAL_FAILED)
|
||||
{
|
||||
DbgOut((DBL_ERROR, "drv2604 auto-calibration failed after %d attempts.\n", nCalibrationCount));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Read calibration results */
|
||||
drv2604_read_reg(AUTO_CALI_RESULT_REG);
|
||||
drv2604_read_reg(AUTO_CALI_BACK_EMF_RESULT_REG);
|
||||
drv2604_read_reg(FEEDBACK_CONTROL_REG);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Read device ID */
|
||||
g_nDeviceID = (status & DEV_ID_MASK);
|
||||
|
||||
switch (g_nDeviceID)
|
||||
{
|
||||
case DRV2605:
|
||||
DbgOut((DBL_INFO, "drv2604 driver found: drv2605.\n"));
|
||||
break;
|
||||
case DRV2604:
|
||||
DbgOut((DBL_INFO, "drv2604 driver found: drv2604.\n"));
|
||||
break;
|
||||
case DRV2604L:
|
||||
DbgOut((DBL_INFO, "drv2604 driver found: drv2604L.\n"));
|
||||
break;
|
||||
case DRV2605L:
|
||||
DbgOut((DBL_INFO, "drv2604 driver found: drv2605L.\n"));
|
||||
break;
|
||||
default:
|
||||
DbgOut((DBL_INFO, "drv2604 driver found: unknown.\n"));
|
||||
break;
|
||||
}
|
||||
|
||||
#if USE_DRV2604_STANDBY
|
||||
/* Put hardware in standby */
|
||||
drv2604_change_mode(MODE_STANDBY);
|
||||
#elif USE_DRV2604_EN_PIN
|
||||
/* enable RTP mode that will be toggled on/off with EN pin */
|
||||
#endif
|
||||
|
||||
#if USE_DRV2604_EN_PIN
|
||||
/* turn off chip */
|
||||
drv2604_set_en(false);
|
||||
#endif
|
||||
|
||||
DbgRecorderInit(());
|
||||
|
||||
ImmVibeSPI_ForceOut_Initialize();
|
||||
VibeOSKernelLinuxInitTimer();
|
||||
ResetOutputData();
|
||||
|
||||
/* Get and concatenate device name and initialize data buffer */
|
||||
g_cchDeviceName = 0;
|
||||
for (i=0; i<NUM_ACTUATORS; i++)
|
||||
{
|
||||
char *szName = g_szDeviceName + g_cchDeviceName;
|
||||
ImmVibeSPI_Device_GetName(i, szName, VIBE_MAX_DEVICE_NAME_LENGTH);
|
||||
|
||||
/* Append version information and get buffer length */
|
||||
strcat(szName, VERSION_STR);
|
||||
g_cchDeviceName += strlen(szName);
|
||||
|
||||
}
|
||||
vibetonz_start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int open(struct inode *inode, struct file *file)
|
||||
{
|
||||
DbgOut((DBL_INFO, "tspdrv: open.\n"));
|
||||
|
||||
if (!try_module_get(THIS_MODULE)) return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int release(struct inode *inode, struct file *file)
|
||||
{
|
||||
DbgOut((DBL_INFO, "tspdrv: release.\n"));
|
||||
|
||||
/*
|
||||
** Reset force and stop timer when the driver is closed, to make sure
|
||||
** no dangling semaphore remains in the system, especially when the
|
||||
** driver is run outside of immvibed for testing purposes.
|
||||
*/
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
|
||||
/*
|
||||
** Clear the variable used to store the magic number to prevent
|
||||
** unauthorized caller to write data. TouchSense service is the only
|
||||
** valid caller.
|
||||
*/
|
||||
file->private_data = (void*)NULL;
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t read(struct file *file, char *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
const size_t nBufSize = (g_cchDeviceName > (size_t)(*ppos)) ? min(count, g_cchDeviceName - (size_t)(*ppos)) : 0;
|
||||
|
||||
/* End of buffer, exit */
|
||||
if (0 == nBufSize) return 0;
|
||||
|
||||
if (0 != copy_to_user(buf, g_szDeviceName + (*ppos), nBufSize))
|
||||
{
|
||||
/* Failed to copy all the data, exit */
|
||||
DbgOut((DBL_ERROR, "tspdrv: copy_to_user failed.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update file position and return copied buffer size */
|
||||
*ppos += nBufSize;
|
||||
return nBufSize;
|
||||
}
|
||||
|
||||
static ssize_t write(struct file *file, const char *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
*ppos = 0; /* file position not used, always set to 0 */
|
||||
|
||||
/*
|
||||
** Prevent unauthorized caller to write data.
|
||||
** TouchSense service is the only valid caller.
|
||||
*/
|
||||
if (file->private_data != (void*)TSPDRV_MAGIC_NUMBER)
|
||||
{
|
||||
DbgOut((DBL_ERROR, "tspdrv: unauthorized write.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Ignore packets that have size smaller than SPI_HEADER_SIZE or bigger than MAX_SPI_BUFFER_SIZE.
|
||||
** Please note that the daemon may send an empty buffer (count == SPI_HEADER_SIZE)
|
||||
** during quiet time between effects while playing a Timeline effect in order to maintain
|
||||
** correct timing: if "count" is equal to SPI_HEADER_SIZE, the call to VibeOSKernelLinuxStartTimer()
|
||||
** will just wait for the next timer tick.
|
||||
*/
|
||||
if ((count < SPI_HEADER_SIZE) || (count > MAX_SPI_BUFFER_SIZE))
|
||||
{
|
||||
DbgOut((DBL_ERROR, "tspdrv: invalid buffer size.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy immediately the input buffer */
|
||||
if (0 != copy_from_user(g_cWriteBuffer, buf, count))
|
||||
{
|
||||
/* Failed to copy all the data, exit */
|
||||
DbgOut((DBL_ERROR, "tspdrv: copy_from_user failed.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Extract force output samples and save them in an internal buffer */
|
||||
if (!SaveOutputData(g_cWriteBuffer, count))
|
||||
{
|
||||
DbgOut((DBL_ERROR, "tspdrv: SaveOutputData failed.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Start the timer after receiving new output force */
|
||||
g_bIsPlaying = true;
|
||||
|
||||
VibeOSKernelLinuxStartTimer();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#if HAVE_UNLOCKED_IOCTL
|
||||
static long unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
#else
|
||||
static int ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
||||
#endif
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case TSPDRV_SET_MAGIC_NUMBER:
|
||||
file->private_data = (void*)TSPDRV_MAGIC_NUMBER;
|
||||
break;
|
||||
|
||||
case TSPDRV_ENABLE_AMP:
|
||||
ImmVibeSPI_ForceOut_AmpEnable(arg);
|
||||
#ifdef VIBE_RUNTIME_RECORD
|
||||
if (atomic_read(&g_bRuntimeRecord)) {
|
||||
DbgRecord((arg,";------- TSPDRV_ENABLE_AMP ---------\n"));
|
||||
}
|
||||
#else
|
||||
DbgRecorderReset((arg));
|
||||
DbgRecord((arg,";------- TSPDRV_ENABLE_AMP ---------\n"));
|
||||
#endif
|
||||
break;
|
||||
|
||||
case TSPDRV_DISABLE_AMP:
|
||||
ImmVibeSPI_ForceOut_AmpDisable(arg);
|
||||
#ifdef VIBE_RUNTIME_RECORD
|
||||
if (atomic_read(&g_bRuntimeRecord)) {
|
||||
DbgRecord((arg,";------- TSPDRV_DISABLE_AMP ---------\n"));
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case TSPDRV_GET_NUM_ACTUATORS:
|
||||
return NUM_ACTUATORS;
|
||||
|
||||
#ifdef IMMVIBESPI_MULTIPARAM_SUPPORT
|
||||
case TSPDRV_GET_PARAM_FILE_ID:
|
||||
return ImmVibeSPI_Device_GetParamFileId();
|
||||
#endif
|
||||
|
||||
case TSPDRV_SET_DBG_LEVEL:
|
||||
{
|
||||
long nDbgLevel;
|
||||
if (0 != copy_from_user((void *)&nDbgLevel, (const void __user *)arg, sizeof(long))) {
|
||||
/* Error copying the data */
|
||||
DbgOut((DBL_ERROR, "copy_from_user failed to copy debug level data.\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (DBL_TEMP <= nDbgLevel && nDbgLevel <= DBL_OVERKILL) {
|
||||
atomic_set(&g_nDebugLevel, nDbgLevel);
|
||||
} else {
|
||||
DbgOut((DBL_ERROR, "Invalid debug level requested, ignored."));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case TSPDRV_GET_DBG_LEVEL:
|
||||
return atomic_read(&g_nDebugLevel);
|
||||
|
||||
#ifdef VIBE_RUNTIME_RECORD
|
||||
case TSPDRV_SET_RUNTIME_RECORD_FLAG:
|
||||
{
|
||||
long nRecordFlag;
|
||||
if (0 != copy_from_user((void *)&nRecordFlag, (const void __user *)arg, sizeof(long))) {
|
||||
/* Error copying the data */
|
||||
DbgOut((DBL_ERROR, "copy_from_user failed to copy runtime record flag.\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
atomic_set(&g_bRuntimeRecord, nRecordFlag);
|
||||
if (nRecordFlag) {
|
||||
int i;
|
||||
for (i=0; i<NUM_ACTUATORS; i++) {
|
||||
DbgRecorderReset((i));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TSPDRV_GET_RUNTIME_RECORD_FLAG:
|
||||
return atomic_read(&g_bRuntimeRecord);
|
||||
case TSPDRV_SET_RUNTIME_RECORD_BUF_SIZE:
|
||||
{
|
||||
long nRecorderBufSize;
|
||||
if (0 != copy_from_user((void *)&nRecorderBufSize, (const void __user *)arg, sizeof(long))) {
|
||||
/* Error copying the data */
|
||||
DbgOut((DBL_ERROR, "copy_from_user failed to copy recorder buffer size.\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 == DbgSetRecordBufferSize(nRecorderBufSize)) {
|
||||
DbgOut((DBL_ERROR, "DbgSetRecordBufferSize failed.\n"));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TSPDRV_GET_RUNTIME_RECORD_BUF_SIZE:
|
||||
return DbgGetRecordBufferSize();
|
||||
#endif
|
||||
|
||||
case TSPDRV_SET_DEVICE_PARAMETER:
|
||||
{
|
||||
device_parameter deviceParam;
|
||||
|
||||
if (0 != copy_from_user((void *)&deviceParam, (const void __user *)arg, sizeof(deviceParam)))
|
||||
{
|
||||
/* Error copying the data */
|
||||
DbgOut((DBL_ERROR, "tspdrv: copy_from_user failed to copy kernel parameter data.\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (deviceParam.nDeviceParamID)
|
||||
{
|
||||
case VIBE_KP_CFG_UPDATE_RATE_MS:
|
||||
/* Update the timer period */
|
||||
g_nTimerPeriodMs = deviceParam.nDeviceParamValue;
|
||||
|
||||
|
||||
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
/* For devices using high resolution timer we need to update the ktime period value */
|
||||
g_ktTimerPeriod = ktime_set(0, g_nTimerPeriodMs * 1000000);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case VIBE_KP_CFG_FREQUENCY_PARAM1:
|
||||
case VIBE_KP_CFG_FREQUENCY_PARAM2:
|
||||
case VIBE_KP_CFG_FREQUENCY_PARAM3:
|
||||
case VIBE_KP_CFG_FREQUENCY_PARAM4:
|
||||
case VIBE_KP_CFG_FREQUENCY_PARAM5:
|
||||
case VIBE_KP_CFG_FREQUENCY_PARAM6:
|
||||
if (0 > ImmVibeSPI_ForceOut_SetFrequency(deviceParam.nDeviceIndex, deviceParam.nDeviceParamID, deviceParam.nDeviceParamValue))
|
||||
{
|
||||
DbgOut((DBL_ERROR, "tspdrv: cannot set device frequency parameter.\n"));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit drv2604_remove(struct i2c_client *client)
|
||||
{
|
||||
DbgOut((KERN_INFO "tspdrv: drv2604_remove_module.\n"));
|
||||
DbgRecorderTerminate(());
|
||||
VibeOSKernelLinuxTerminateTimer();
|
||||
ImmVibeSPI_ForceOut_Terminate();
|
||||
misc_deregister(&miscdev);
|
||||
gpio_free(vibrator_drvdata.motor_en);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int drv2604_vibrator_suspend(struct i2c_client *client,pm_message_t mesg)
|
||||
{
|
||||
|
||||
int ret = 0;
|
||||
if (g_bIsPlaying){
|
||||
ret = -EBUSY;
|
||||
}
|
||||
else{
|
||||
DbgOut((KERN_INFO "tspdrv: suspend.\n"));
|
||||
if(drv2604_enabled){
|
||||
gpio_set_value(vibrator_drvdata.motor_en, VIBRATION_OFF);
|
||||
drv2604_enabled = 0;
|
||||
}
|
||||
DbgOut((KERN_ERR "[VIBTONZ] drv2604_early_suspend \n"));
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int drv2604_vibrator_resume(struct i2c_client *client)
|
||||
{
|
||||
|
||||
DbgOut(KERN_DEBUG "[VIBTONZ] drv2604_vibrator_resume \n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id drv2604_vibrator_device_id[] = {
|
||||
{"drv2604_vibrator", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, drv2604_vibrator_device_id);
|
||||
static struct of_device_id drv2604_vibrator_table[] = {
|
||||
|
||||
{ .compatible = "drv2604_vibrator,vibrator",},
|
||||
{ },
|
||||
};
|
||||
|
||||
|
||||
static struct i2c_driver drv2604_vibrator_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "drv2604_vibrator",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = drv2604_vibrator_table,
|
||||
},
|
||||
.probe = drv2604_vibrator_i2c_probe,
|
||||
.id_table = drv2604_vibrator_device_id,
|
||||
.suspend = drv2604_vibrator_suspend,
|
||||
.resume = drv2604_vibrator_resume,
|
||||
.remove = __devexit_p(drv2604_remove),
|
||||
};
|
||||
|
||||
static int __init drv2604_vibrator_init(void)
|
||||
{
|
||||
return(i2c_add_driver(&drv2604_vibrator_i2c_driver));
|
||||
}
|
||||
|
||||
static void __exit drv2604_vibrator_exit(void)
|
||||
{
|
||||
i2c_del_driver(&drv2604_vibrator_i2c_driver);
|
||||
}
|
||||
|
||||
module_init(drv2604_vibrator_init);
|
||||
module_exit(drv2604_vibrator_exit);
|
||||
|
||||
MODULE_AUTHOR("Immersion Corporation");
|
||||
MODULE_DESCRIPTION("TouchSense Kernel Module");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -1,695 +0,0 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** tspdrv_drv2604.h
|
||||
**
|
||||
** Description:
|
||||
** Constants and type definitions for the TouchSense Kernel Module.
|
||||
**
|
||||
** Portions Copyright (c) 2008-2010 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** =========================================================================
|
||||
*/
|
||||
|
||||
#ifndef _TSPDRV_H
|
||||
#define _TSPDRV_H
|
||||
|
||||
/* Constants */
|
||||
#define MODULE_NAME "tspdrv"
|
||||
#define TSPDRV "/dev/"MODULE_NAME
|
||||
#define TSPDRV_MAGIC_NUMBER 0x494D4D52
|
||||
|
||||
#define TSPDRV_IOCTL_GROUP 0x52
|
||||
#define TSPDRV_STOP_KERNEL_TIMER _IO(TSPDRV_IOCTL_GROUP, 1) /* obsolete, may be removed in future */
|
||||
#define TSPDRV_SET_MAGIC_NUMBER _IO(TSPDRV_IOCTL_GROUP, 2)
|
||||
#define TSPDRV_ENABLE_AMP _IO(TSPDRV_IOCTL_GROUP, 3)
|
||||
#define TSPDRV_DISABLE_AMP _IO(TSPDRV_IOCTL_GROUP, 4)
|
||||
#define TSPDRV_GET_NUM_ACTUATORS _IO(TSPDRV_IOCTL_GROUP, 5)
|
||||
#define TSPDRV_SET_DEVICE_PARAMETER _IO(TSPDRV_IOCTL_GROUP, 6)
|
||||
#define TSPDRV_SET_DBG_LEVEL _IO(TSPDRV_IOCTL_GROUP, 7)
|
||||
#define TSPDRV_GET_DBG_LEVEL _IO(TSPDRV_IOCTL_GROUP, 8)
|
||||
#define TSPDRV_SET_RUNTIME_RECORD_FLAG _IO(TSPDRV_IOCTL_GROUP, 9)
|
||||
#define TSPDRV_GET_RUNTIME_RECORD_FLAG _IO(TSPDRV_IOCTL_GROUP, 10)
|
||||
#define TSPDRV_SET_RUNTIME_RECORD_BUF_SIZE _IO(TSPDRV_IOCTL_GROUP, 11)
|
||||
#define TSPDRV_GET_RUNTIME_RECORD_BUF_SIZE _IO(TSPDRV_IOCTL_GROUP, 12)
|
||||
#define TSPDRV_GET_PARAM_FILE_ID _IO(TSPDRV_IOCTL_GROUP, 13)
|
||||
/*
|
||||
** Frequency constant parameters to control force output values and signals.
|
||||
*/
|
||||
#define VIBE_KP_CFG_FREQUENCY_PARAM1 85
|
||||
#define VIBE_KP_CFG_FREQUENCY_PARAM2 86
|
||||
#define VIBE_KP_CFG_FREQUENCY_PARAM3 87
|
||||
#define VIBE_KP_CFG_FREQUENCY_PARAM4 88
|
||||
#define VIBE_KP_CFG_FREQUENCY_PARAM5 89
|
||||
#define VIBE_KP_CFG_FREQUENCY_PARAM6 90
|
||||
|
||||
/*
|
||||
** Force update rate in milliseconds.
|
||||
*/
|
||||
#define VIBE_KP_CFG_UPDATE_RATE_MS 95
|
||||
|
||||
#define VIBE_MAX_DEVICE_NAME_LENGTH 64
|
||||
#define SPI_HEADER_SIZE 3 /* DO NOT CHANGE - SPI buffer header size */
|
||||
#define VIBE_OUTPUT_SAMPLE_SIZE 50 /* DO NOT CHANGE - maximum number of samples */
|
||||
#define MAX_DEBUG_BUFFER_LENGTH 1024
|
||||
|
||||
|
||||
|
||||
/* Type definitions */
|
||||
#ifdef __KERNEL__
|
||||
typedef int8_t VibeInt8;
|
||||
typedef u_int8_t VibeUInt8;
|
||||
typedef int16_t VibeInt16;
|
||||
typedef u_int16_t VibeUInt16;
|
||||
typedef int32_t VibeInt32;
|
||||
typedef u_int32_t VibeUInt32;
|
||||
typedef u_int8_t VibeBool;
|
||||
typedef VibeInt32 VibeStatus;
|
||||
/* Debug Levels */
|
||||
#define DBL_TEMP 0
|
||||
#define DBL_FATAL 0
|
||||
#define DBL_ERROR 1
|
||||
#define DBL_WARNING 2
|
||||
#define DBL_INFO 3
|
||||
#define DBL_VERBOSE 4
|
||||
#define DBL_OVERKILL 5
|
||||
|
||||
/* Device parameters sent to the kernel module, tspdrv.ko */
|
||||
typedef struct
|
||||
{
|
||||
VibeInt32 nDeviceIndex;
|
||||
VibeInt32 nDeviceParamID;
|
||||
VibeInt32 nDeviceParamValue;
|
||||
} device_parameter;
|
||||
|
||||
#endif
|
||||
|
||||
/* Error and Return value codes */
|
||||
#define VIBE_S_SUCCESS 0 /* Success */
|
||||
#define VIBE_E_FAIL -4 /* Generic error */
|
||||
|
||||
|
||||
#define VIBRATION_ON 1
|
||||
#define VIBRATION_OFF 0
|
||||
|
||||
|
||||
#if defined(VIBE_RECORD) && defined(VIBE_DEBUG)
|
||||
void _RecorderInit(void);
|
||||
void _RecorderTerminate(void);
|
||||
void _RecorderReset(int nActuator);
|
||||
void _Record(int actuatorIndex, const char *format,...);
|
||||
#endif
|
||||
|
||||
/* Kernel Debug Macros */
|
||||
#ifdef __KERNEL__
|
||||
#ifdef VIBE_DEBUG
|
||||
#define DbgOut(_x_) printk _x_
|
||||
#else /* VIBE_DEBUG */
|
||||
#define DbgOut(_x_)
|
||||
#endif /* VIBE_DEBUG */
|
||||
|
||||
#if defined(VIBE_RECORD) && defined(VIBE_DEBUG)
|
||||
#define DbgRecorderInit(_x_) _RecorderInit _x_
|
||||
#define DbgRecorderTerminate(_x_) _RecorderTerminate _x_
|
||||
#define DbgRecorderReset(_x_) _RecorderReset _x_
|
||||
#define DbgRecord(_x_) _Record _x_
|
||||
#else /* defined(VIBE_RECORD) && defined(VIBE_DEBUG) */
|
||||
#define DbgRecorderInit(_x_)
|
||||
#define DbgRecorderTerminate(_x_)
|
||||
#define DbgRecorderReset(_x_)
|
||||
#define DbgRecord(_x_)
|
||||
#endif /* defined(VIBE_RECORD) && defined(VIBE_DEBUG) */
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define GUARANTEE_AUTOTUNE_BRAKE_TIME 1
|
||||
|
||||
/*
|
||||
** Enable to use DRV2604 EN pin to enter standby mode
|
||||
*/
|
||||
#define USE_DRV2604_EN_PIN 1
|
||||
|
||||
/*
|
||||
** Enable to use DRV2604 i2c command to enter standby mode
|
||||
*/
|
||||
#define USE_DRV2604_STANDBY 1
|
||||
|
||||
/*
|
||||
** This SPI supports only one actuator.
|
||||
*/
|
||||
#define NUM_ACTUATORS 1
|
||||
|
||||
/*
|
||||
** Name of the DRV2604 board
|
||||
*/
|
||||
#define DRV2604_BOARD_NAME "DRV2604L"
|
||||
#define HAPTIC_I2C_ID 15
|
||||
/*
|
||||
** Go
|
||||
*/
|
||||
#define GO_REG 0x0C
|
||||
#define GO 0x01
|
||||
#define STOP 0x00
|
||||
|
||||
/*
|
||||
** Status
|
||||
*/
|
||||
#define STATUS_REG 0x00
|
||||
#define STATUS_DEFAULT 0x00
|
||||
|
||||
#define DIAG_RESULT_MASK (1 << 3)
|
||||
#define AUTO_CAL_PASSED (0 << 3)
|
||||
#define AUTO_CAL_FAILED (1 << 3)
|
||||
#define DIAG_GOOD (0 << 3)
|
||||
#define DIAG_BAD (1 << 3)
|
||||
|
||||
#define DEV_ID_MASK (7 << 5)
|
||||
#define DRV2605 (3 << 5) /* per DRV2604 datasheet */
|
||||
#define DRV2604 (4 << 5)
|
||||
#define DRV2605L (7<<5)
|
||||
#define DRV2604L (6<<5)
|
||||
/*
|
||||
** Mode
|
||||
*/
|
||||
#define MODE_REG 0x01
|
||||
#define MODE_STANDBY 0x40
|
||||
#define MODE_DEVICE_READY 0x00
|
||||
|
||||
#define DRV2604_MODE_MASK 0x07
|
||||
#define MODE_INTERNAL_TRIGGER 0
|
||||
#define MODE_REAL_TIME_PLAYBACK 5
|
||||
#define MODE_DIAGNOSTICS 6
|
||||
#define AUTO_CALIBRATION 7
|
||||
|
||||
#define MODE_RESET 0x80
|
||||
|
||||
/*
|
||||
** Real Time Playback
|
||||
*/
|
||||
#define REAL_TIME_PLAYBACK_REG 0x02
|
||||
|
||||
/*
|
||||
** Library Selection
|
||||
*/
|
||||
#define LIBRARY_SELECTION_REG 0x03
|
||||
#define LIBRARY_SELECTION_DEFAULT 0x00
|
||||
|
||||
/*
|
||||
** Waveform Sequencer
|
||||
*/
|
||||
#define WAVEFORM_SEQUENCER_REG 0x04
|
||||
#define WAVEFORM_SEQUENCER_REG2 0x05
|
||||
#define WAVEFORM_SEQUENCER_REG3 0x06
|
||||
#define WAVEFORM_SEQUENCER_REG4 0x07
|
||||
#define WAVEFORM_SEQUENCER_REG5 0x08
|
||||
#define WAVEFORM_SEQUENCER_REG6 0x09
|
||||
#define WAVEFORM_SEQUENCER_REG7 0x0A
|
||||
#define WAVEFORM_SEQUENCER_REG8 0x0B
|
||||
#define WAVEFORM_SEQUENCER_MAX 8
|
||||
#define WAVEFORM_SEQUENCER_DEFAULT 0x00
|
||||
|
||||
/*
|
||||
** OverDrive Time Offset
|
||||
*/
|
||||
#define OVERDRIVE_TIME_OFFSET_REG 0x0D
|
||||
|
||||
/*
|
||||
** Sustain Time Offset, postive
|
||||
*/
|
||||
#define SUSTAIN_TIME_OFFSET_POS_REG 0x0E
|
||||
|
||||
/*
|
||||
** Sustain Time Offset, negative
|
||||
*/
|
||||
#define SUSTAIN_TIME_OFFSET_NEG_REG 0x0F
|
||||
|
||||
/*
|
||||
** Brake Time Offset
|
||||
*/
|
||||
#define BRAKE_TIME_OFFSET_REG 0x10
|
||||
|
||||
/*
|
||||
** Rated Voltage
|
||||
*/
|
||||
#define RATED_VOLTAGE_REG 0x16
|
||||
|
||||
/*
|
||||
** Overdrive Clamp Voltage
|
||||
*/
|
||||
#define OVERDRIVE_CLAMP_VOLTAGE_REG 0x17
|
||||
|
||||
/*
|
||||
** Auto Calibrationi Compensation Result
|
||||
*/
|
||||
#define AUTO_CALI_RESULT_REG 0x18
|
||||
|
||||
/*
|
||||
** Auto Calibration Back-EMF Result
|
||||
*/
|
||||
#define AUTO_CALI_BACK_EMF_RESULT_REG 0x19
|
||||
|
||||
/*
|
||||
** Feedback Control
|
||||
*/
|
||||
#define FEEDBACK_CONTROL_REG 0x1A
|
||||
|
||||
#define FEEDBACK_CONTROL_BEMF_LRA_GAIN0 0 /* 5x */
|
||||
#define FEEDBACK_CONTROL_BEMF_LRA_GAIN1 1 /* 10x */
|
||||
#define FEEDBACK_CONTROL_BEMF_LRA_GAIN2 2 /* 20x */
|
||||
#define FEEDBACK_CONTROL_BEMF_LRA_GAIN3 3 /* 30x */
|
||||
|
||||
#define LOOP_RESPONSE_SLOW (0 << 2)
|
||||
#define LOOP_RESPONSE_MEDIUM (1 << 2) /* default */
|
||||
#define LOOP_RESPONSE_FAST (2 << 2)
|
||||
#define LOOP_RESPONSE_VERY_FAST (3 << 2)
|
||||
|
||||
#define FB_BRAKE_FACTOR_1X (0 << 4) /* 1x */
|
||||
#define FB_BRAKE_FACTOR_2X (1 << 4) /* 2x */
|
||||
#define FB_BRAKE_FACTOR_3X (2 << 4) /* 3x (default) */
|
||||
#define FB_BRAKE_FACTOR_4X (3 << 4) /* 4x */
|
||||
#define FB_BRAKE_FACTOR_6X (4 << 4) /* 6x */
|
||||
#define FB_BRAKE_FACTOR_8X (5 << 4) /* 8x */
|
||||
#define FB_BRAKE_FACTOR_16X (6 << 4) /* 16x */
|
||||
#define FB_BRAKE_DISABLED (7 << 4)
|
||||
|
||||
#define FEEDBACK_CONTROL_MODE_ERM 0 /* default */
|
||||
#define FEEDBACK_CONTROL_MODE_LRA (1 << 7)
|
||||
|
||||
/*
|
||||
** Control1
|
||||
*/
|
||||
#define Control1_REG 0x1B
|
||||
#define STARTUP_BOOST_ENABLED (1 << 7)
|
||||
#define STARTUP_BOOST_DISABLED (0 << 7) /* default */
|
||||
|
||||
/*
|
||||
** Control2
|
||||
*/
|
||||
#define Control2_REG 0x1C
|
||||
|
||||
#define IDISS_TIME_MASK 0x03
|
||||
#define IDISS_TIME_VERY_SHORT 0
|
||||
#define IDISS_TIME_SHORT 1
|
||||
#define IDISS_TIME_MEDIUM 2 /* default */
|
||||
#define IDISS_TIME_LONG 3
|
||||
|
||||
#define BLANKING_TIME_MASK 0x0C
|
||||
#define BLANKING_TIME_VERY_SHORT (0 << 2)
|
||||
#define BLANKING_TIME_SHORT (1 << 2)
|
||||
#define BLANKING_TIME_MEDIUM (2 << 2) /* default */
|
||||
#define BLANKING_TIME_VERY_LONG (3 << 2)
|
||||
|
||||
#define AUTO_RES_GAIN_MASK 0x30
|
||||
#define AUTO_RES_GAIN_VERY_LOW (0 << 4)
|
||||
#define AUTO_RES_GAIN_LOW (1 << 4)
|
||||
#define AUTO_RES_GAIN_MEDIUM (2 << 4) /* default */
|
||||
#define AUTO_RES_GAIN_HIGH (3 << 4)
|
||||
|
||||
#define SOFT_BRAKE_MASK 0x40
|
||||
#define SOFT_BRAKE (1 << 6)
|
||||
|
||||
#define BIDIR_INPUT_MASK 0x80
|
||||
#define UNIDIRECT_INPUT (0 << 7)
|
||||
#define BIDIRECT_INPUT (1 << 7) /* default */
|
||||
|
||||
/*
|
||||
** Control3
|
||||
*/
|
||||
#define Control3_REG 0x1D
|
||||
|
||||
#define ERM_OpenLoop_Enabled (1 << 5)
|
||||
#define NG_Thresh_1 (1 << 6)
|
||||
#define NG_Thresh_2 (2 << 6)
|
||||
#define NG_Thresh_3 (3 << 6)
|
||||
|
||||
/*
|
||||
** Auto Calibration Memory Interface
|
||||
*/
|
||||
#define AUTOCAL_MEM_INTERFACE_REG 0x1E
|
||||
|
||||
#define AUTOCAL_TIME_150MS (0 << 4)
|
||||
#define AUTOCAL_TIME_250MS (1 << 4)
|
||||
#define AUTOCAL_TIME_500MS (2 << 4)
|
||||
#define AUTOCAL_TIME_1000MS (3 << 4)
|
||||
|
||||
#define SILICON_REVISION_REG 0x3B
|
||||
#define SILICON_REVISION_MASK 0x07
|
||||
|
||||
#define DEFAULT_DRIVE_TIME 0x17
|
||||
|
||||
#define MAX_AUTOCALIBRATION_ATTEMPT 2
|
||||
#define SKIP_AUTOCAL 1
|
||||
#define GO_BIT_POLL_INTERVAL 15
|
||||
|
||||
#define MAX_REVISION_STRING_SIZE 20
|
||||
|
||||
#ifndef GPIO_LEVEL_HIGH
|
||||
#define GPIO_LEVEL_HIGH 1
|
||||
#endif
|
||||
#ifndef GPIO_LEVEL_LOW
|
||||
#define GPIO_LEVEL_LOW 0
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////
|
||||
|
||||
#define DEVICE_NAME "DRV2604L"
|
||||
#define DRIVER_VERSION "162"
|
||||
|
||||
#define EFFECT_LIBRARY LIBRARY_A
|
||||
|
||||
/* Commands */
|
||||
#define HAPTIC_CMDID_PLAY_SINGLE_EFFECT 0x01
|
||||
#define HAPTIC_CMDID_PLAY_EFFECT_SEQUENCE 0x02
|
||||
#define HAPTIC_CMDID_PLAY_TIMED_EFFECT 0x03
|
||||
#define HAPTIC_CMDID_GET_DEV_ID 0x04
|
||||
#define HAPTIC_CMDID_RUN_DIAG 0x05
|
||||
#define HAPTIC_CMDID_AUDIOHAPTIC_ENABLE 0x06
|
||||
#define HAPTIC_CMDID_AUDIOHAPTIC_DISABLE 0x07
|
||||
#define HAPTIC_CMDID_AUDIOHAPTIC_GETSTATUS 0x08
|
||||
#define HAPTIC_CMDID_STOP 0xFF
|
||||
|
||||
/* Command size */
|
||||
#define HAPTIC_CMDSZ_SINGLE_EFFECT 2
|
||||
#define HAPTIC_CMDSZ_EFFECT_SEQUENCE 9
|
||||
#define HAPTIC_CMDSZ_TIMED_EFFECT 3
|
||||
#define HAPTIC_CMDSZ_STOP 1
|
||||
|
||||
/*
|
||||
** Go
|
||||
*/
|
||||
#define GO_REG 0x0C
|
||||
#define GO 0x01
|
||||
#define STOP 0x00
|
||||
|
||||
/*
|
||||
** Status
|
||||
*/
|
||||
#define STATUS_REG 0x00
|
||||
#define STATUS_DEFAULT 0x00
|
||||
|
||||
#define DIAG_RESULT_MASK (1 << 3)
|
||||
#define AUTO_CAL_PASSED (0 << 3)
|
||||
#define AUTO_CAL_FAILED (1 << 3)
|
||||
#define DIAG_GOOD (0 << 3)
|
||||
#define DIAG_BAD (1 << 3)
|
||||
|
||||
#define DEV_ID_MASK (7 << 5)
|
||||
//#define DRV2605 (5 << 5)
|
||||
#define DRV2604 (4 << 5)
|
||||
|
||||
/*
|
||||
** Mode
|
||||
*/
|
||||
#define MODE_REG 0x01
|
||||
#define MODE_STANDBY 0x40
|
||||
|
||||
#define DRV260X_MODE_MASK 0x07
|
||||
#define MODE_INTERNAL_TRIGGER 0
|
||||
#define MODE_EXTERNAL_TRIGGER_EDGE 1
|
||||
#define MODE_EXTERNAL_TRIGGER_LEVEL 2
|
||||
#define MODE_PWM_OR_ANALOG_INPUT 3
|
||||
#define MODE_AUDIOHAPTIC 4
|
||||
#define MODE_REAL_TIME_PLAYBACK 5
|
||||
#define MODE_DIAGNOSTICS 6
|
||||
#define AUTO_CALIBRATION 7
|
||||
|
||||
#define MODE_STANDBY_MASK 0x40
|
||||
#define MODE_READY 1 // default
|
||||
#define MODE_SOFT_STANDBY 0
|
||||
|
||||
#define MODE_RESET 0x80
|
||||
|
||||
/*
|
||||
** Real Time Playback
|
||||
*/
|
||||
#define REAL_TIME_PLAYBACK_REG 0x02
|
||||
|
||||
/*
|
||||
** Library Selection
|
||||
*/
|
||||
#define LIBRARY_SELECTION_REG 0x03
|
||||
#define LIBRARY_SELECTION_DEFAULT 0x00
|
||||
|
||||
#define LIBRARY_A 0x01
|
||||
#define LIBRARY_B 0x02
|
||||
#define LIBRARY_C 0x03
|
||||
#define LIBRARY_D 0x04
|
||||
#define LIBRARY_E 0x05
|
||||
#define LIBRARY_F 0x06
|
||||
|
||||
#define LIBRARY_SELECTION_MASK 0x07
|
||||
#define LIBRARY_SELECTION_LIBRARY_RAM 0 // default
|
||||
#define LIBRARY_SELECTION_LIBRARY_OVERDRIVE 1
|
||||
#define LIBRARY_SELECTION_LIBRARY_40_60 2
|
||||
#define LIBRARY_SELECTION_LIBRARY_60_80 3
|
||||
#define LIBRARY_SELECTION_LIBRARY_100_140 4
|
||||
#define LIBRARY_SELECTION_LIBRARY_140_PLUS 5
|
||||
|
||||
#define LIBRARY_SELECTION_HIZ_MASK 0x10
|
||||
#define LIBRARY_SELECTION_HIZ_EN 1
|
||||
#define LIBRARY_SELECTION_HIZ_DIS 0
|
||||
|
||||
/*
|
||||
** Waveform Sequencer
|
||||
*/
|
||||
#define WAVEFORM_SEQUENCER_REG 0x04
|
||||
#define WAVEFORM_SEQUENCER_REG2 0x05
|
||||
#define WAVEFORM_SEQUENCER_REG3 0x06
|
||||
#define WAVEFORM_SEQUENCER_REG4 0x07
|
||||
#define WAVEFORM_SEQUENCER_REG5 0x08
|
||||
#define WAVEFORM_SEQUENCER_REG6 0x09
|
||||
#define WAVEFORM_SEQUENCER_REG7 0x0A
|
||||
#define WAVEFORM_SEQUENCER_REG8 0x0B
|
||||
#define WAVEFORM_SEQUENCER_MAX 8
|
||||
#define WAVEFORM_SEQUENCER_DEFAULT 0x00
|
||||
|
||||
/*
|
||||
** OverDrive Time Offset
|
||||
*/
|
||||
#define OVERDRIVE_TIME_OFFSET_REG 0x0D
|
||||
|
||||
/*
|
||||
** Sustain Time Offset, postive
|
||||
*/
|
||||
#define SUSTAIN_TIME_OFFSET_POS_REG 0x0E
|
||||
|
||||
/*
|
||||
** Sustain Time Offset, negative
|
||||
*/
|
||||
#define SUSTAIN_TIME_OFFSET_NEG_REG 0x0F
|
||||
|
||||
/*
|
||||
** Brake Time Offset
|
||||
*/
|
||||
#define BRAKE_TIME_OFFSET_REG 0x10
|
||||
|
||||
/*
|
||||
** Audio to Haptics Control
|
||||
*/
|
||||
#define AUDIO_HAPTICS_CONTROL_REG 0x11
|
||||
|
||||
#define AUDIO_HAPTICS_RECT_10MS (0 << 2)
|
||||
#define AUDIO_HAPTICS_RECT_20MS (1 << 2)
|
||||
#define AUDIO_HAPTICS_RECT_30MS (2 << 2)
|
||||
#define AUDIO_HAPTICS_RECT_40MS (3 << 2)
|
||||
|
||||
#define AUDIO_HAPTICS_FILTER_100HZ 0
|
||||
#define AUDIO_HAPTICS_FILTER_125HZ 1
|
||||
#define AUDIO_HAPTICS_FILTER_150HZ 2
|
||||
#define AUDIO_HAPTICS_FILTER_200HZ 3
|
||||
|
||||
/*
|
||||
** Audio to Haptics Minimum Input Level
|
||||
*/
|
||||
#define AUDIO_HAPTICS_MIN_INPUT_REG 0x12
|
||||
|
||||
/*
|
||||
** Audio to Haptics Maximum Input Level
|
||||
*/
|
||||
#define AUDIO_HAPTICS_MAX_INPUT_REG 0x13
|
||||
|
||||
/*
|
||||
** Audio to Haptics Minimum Output Drive
|
||||
*/
|
||||
#define AUDIO_HAPTICS_MIN_OUTPUT_REG 0x14
|
||||
|
||||
/*
|
||||
** Audio to Haptics Maximum Output Drive
|
||||
*/
|
||||
#define AUDIO_HAPTICS_MAX_OUTPUT_REG 0x15
|
||||
|
||||
/*
|
||||
** Rated Voltage
|
||||
*/
|
||||
#define RATED_VOLTAGE_REG 0x16
|
||||
|
||||
/*
|
||||
** Overdrive Clamp Voltage
|
||||
*/
|
||||
#define OVERDRIVE_CLAMP_VOLTAGE_REG 0x17
|
||||
|
||||
/*
|
||||
** Auto Calibrationi Compensation Result
|
||||
*/
|
||||
#define AUTO_CALI_RESULT_REG 0x18
|
||||
|
||||
/*
|
||||
** Auto Calibration Back-EMF Result
|
||||
*/
|
||||
#define AUTO_CALI_BACK_EMF_RESULT_REG 0x19
|
||||
|
||||
/*
|
||||
** Feedback Control
|
||||
*/
|
||||
|
||||
|
||||
#if EFFECT_LIBRARY == LIBRARY_A
|
||||
#define REAL_TIME_PLAYBACK_STRENGTH 0x38 // ~44% of overdrive voltage (open loop)
|
||||
#elif EFFECT_LIBRARY == LIBRARY_F
|
||||
#define REAL_TIME_PLAYBACK_STRENGTH LRA_RTP_STRENGTH
|
||||
#else
|
||||
#define REAL_TIME_PLAYBACK_STRENGTH 0x7F // 100% of overdrive voltage (open loop)
|
||||
#endif
|
||||
|
||||
#define FEEDBACK_CONTROL_REG 0x1A
|
||||
|
||||
#define FEEDBACK_CONTROL_BEMF_ERM_GAIN0 0 // 0.33x
|
||||
#define FEEDBACK_CONTROL_BEMF_ERM_GAIN1 1 // 1.0x
|
||||
#define FEEDBACK_CONTROL_BEMF_ERM_GAIN2 2 // 1.8x
|
||||
#define FEEDBACK_CONTROL_BEMF_ERM_GAIN3 3 // 4.0x
|
||||
|
||||
#define FEEDBACK_CONTROL_BEMF_LRA_GAIN0 0 // 5x
|
||||
#define FEEDBACK_CONTROL_BEMF_LRA_GAIN1 1 // 10x
|
||||
#define FEEDBACK_CONTROL_BEMF_LRA_GAIN2 2 // 20x
|
||||
#define FEEDBACK_CONTROL_BEMF_LRA_GAIN3 3 // 30x
|
||||
|
||||
#define LOOP_RESPONSE_SLOW (0 << 2)
|
||||
#define LOOP_RESPONSE_MEDIUM (1 << 2) // default
|
||||
#define LOOP_RESPONSE_FAST (2 << 2)
|
||||
#define LOOP_RESPONSE_VERY_FAST (3 << 2)
|
||||
|
||||
#define FB_BRAKE_FACTOR_1X (0 << 4) // 1x
|
||||
#define FB_BRAKE_FACTOR_2X (1 << 4) // 2x
|
||||
#define FB_BRAKE_FACTOR_3X (2 << 4) // 3x (default)
|
||||
#define FB_BRAKE_FACTOR_4X (3 << 4) // 4x
|
||||
#define FB_BRAKE_FACTOR_6X (4 << 4) // 6x
|
||||
#define FB_BRAKE_FACTOR_8X (5 << 4) // 8x
|
||||
#define FB_BRAKE_FACTOR_16X (6 << 4) // 16x
|
||||
#define FB_BRAKE_DISABLED (7 << 4)
|
||||
|
||||
#define FEEDBACK_CONTROL_MODE_ERM 0 // default
|
||||
#define FEEDBACK_CONTROL_MODE_LRA (1 << 7)
|
||||
|
||||
#define LRA_DRIVE_TIME 0x13
|
||||
#define LRA_SEMCO1036 0
|
||||
#define LRA_SEMCO0934 1
|
||||
#define LRA_SELECTION LRA_SEMCO1036
|
||||
#if (LRA_SELECTION == LRA_SEMCO1036)
|
||||
#define LRA_RATED_VOLTAGE 0x56
|
||||
#define LRA_OVERDRIVE_CLAMP_VOLTAGE 0x90
|
||||
#define LRA_RTP_STRENGTH 0x7F // 100% of rated voltage (closed loop)
|
||||
|
||||
#elif (LRA_SELECTION == LRA_SEMCO0934)
|
||||
#define LRA_RATED_VOLTAGE 0x51
|
||||
#define LRA_OVERDRIVE_CLAMP_VOLTAGE 0x72
|
||||
#define LRA_RTP_STRENGTH 0x7F // 100% of rated voltage (closed loop)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Control1
|
||||
*/
|
||||
#define Control1_REG 0x1B
|
||||
|
||||
#define STARTUP_BOOST_ENABLED (1 << 7)
|
||||
#define STARTUP_BOOST_DISABLED (0 << 7) // default
|
||||
#define AC_COUPLE_ENABLED (1 << 5)
|
||||
#define AC_COUPLE_DISABLED (0 << 5) // default
|
||||
|
||||
#define DEFAULT_DRIVE_TIME 0x17
|
||||
#define AUDIOHAPTIC_DRIVE_TIME 0x13
|
||||
|
||||
/*
|
||||
** Control2
|
||||
*/
|
||||
#define Control2_REG 0x1C
|
||||
|
||||
#define IDISS_TIME_MASK 0x03
|
||||
#define IDISS_TIME_VERY_SHORT 0
|
||||
#define IDISS_TIME_SHORT 1
|
||||
#define IDISS_TIME_MEDIUM 2 // default
|
||||
#define IDISS_TIME_LONG 3
|
||||
|
||||
#define BLANKING_TIME_MASK 0x0C
|
||||
#define BLANKING_TIME_VERY_SHORT (0 << 2)
|
||||
#define BLANKING_TIME_SHORT (1 << 2)
|
||||
#define BLANKING_TIME_MEDIUM (2 << 2) // default
|
||||
#define BLANKING_TIME_VERY_LONG (3 << 2)
|
||||
|
||||
#define AUTO_RES_GAIN_MASK 0x30
|
||||
#define AUTO_RES_GAIN_VERY_LOW (0 << 4)
|
||||
#define AUTO_RES_GAIN_LOW (1 << 4)
|
||||
#define AUTO_RES_GAIN_MEDIUM (2 << 4) // default
|
||||
#define AUTO_RES_GAIN_HIGH (3 << 4)
|
||||
|
||||
#define SOFT_BRAKE_MASK 0x40
|
||||
|
||||
#define BIDIR_INPUT_MASK 0x80
|
||||
#define UNIDIRECT_INPUT (0 << 7)
|
||||
#define BIDIRECT_INPUT (1 << 7) // default
|
||||
|
||||
/*
|
||||
** Control3
|
||||
*/
|
||||
#define Control3_REG 0x1D
|
||||
|
||||
#define INPUT_PWM (0 << 1) // default
|
||||
#define INPUT_ANALOG (1 << 1)
|
||||
#define ERM_OpenLoop_Enabled (1 << 5)
|
||||
#define NG_Thresh_DISABLED (0 << 6)
|
||||
#define NG_Thresh_1 (1 << 6)
|
||||
#define NG_Thresh_2 (2 << 6)
|
||||
#define NG_Thresh_3 (3 << 6)
|
||||
|
||||
/*
|
||||
** Auto Calibration Memory Interface
|
||||
*/
|
||||
#define AUTOCAL_MEM_INTERFACE_REG 0x1E
|
||||
|
||||
#define AUTOCAL_TIME_150MS (0 << 4)
|
||||
#define AUTOCAL_TIME_250MS (1 << 4)
|
||||
#define AUTOCAL_TIME_500MS (2 << 4)
|
||||
#define AUTOCAL_TIME_1000MS (3 << 4)
|
||||
|
||||
#define SILICON_REVISION_REG 0x3B
|
||||
#define SILICON_REVISION_MASK 0x07
|
||||
|
||||
#define AUDIO_HAPTICS_MIN_INPUT_VOLTAGE 0x19
|
||||
#define AUDIO_HAPTICS_MAX_INPUT_VOLTAGE 0x64
|
||||
#define AUDIO_HAPTICS_MIN_OUTPUT_VOLTAGE 0x19
|
||||
#define AUDIO_HAPTICS_MAX_OUTPUT_VOLTAGE 0xFF
|
||||
|
||||
#define DEFAULT_ERM_AUTOCAL_COMPENSATION 0x14
|
||||
#define DEFAULT_ERM_AUTOCAL_BACKEMF 0x72
|
||||
|
||||
#define DEFAULT_LRA_AUTOCAL_COMPENSATION 0x06
|
||||
#define DEFAULT_LRA_AUTOCAL_BACKEMF 0xDE
|
||||
|
||||
int vibrator_write_register(u8 addr, u8 w_data);
|
||||
|
||||
extern struct vibrator_platform_data_drv2604 vibrator_drvdata;
|
||||
#endif /* _TSPDRV_H */
|
|
@ -1,725 +0,0 @@
|
|||
/*
|
||||
** =========================================================================
|
||||
** File:
|
||||
** tspdrv_isa1200.c
|
||||
**
|
||||
** Description:
|
||||
** TouchSense Kernel Module main entry-point.
|
||||
**
|
||||
** Portions Copyright (c) 2008-2010 Immersion Corporation. All Rights Reserved.
|
||||
**
|
||||
** This file contains Original Code and/or Modifications of Original Code
|
||||
** as defined in and that are subject to the GNU Public License v2 -
|
||||
** (the 'License'). You may not use this file except in compliance with the
|
||||
** License. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA or contact
|
||||
** TouchSenseSales@immersion.com.
|
||||
**
|
||||
** The Original Code and all software distributed under the License are
|
||||
** distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
** EXPRESS OR IMPLIED, AND IMMERSION HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
** INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
** FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
|
||||
** the License for the specific language governing rights and limitations
|
||||
** under the License.
|
||||
** =========================================================================
|
||||
*/
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#define __KERNEL__
|
||||
#endif
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include "../staging/android/timed_output.h"
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/vibrator.h>
|
||||
#include "tspdrv_isa1200.h"
|
||||
#include "ImmVibeSPI_isa1200.c"
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/earlysuspend.h>
|
||||
#if defined(VIBE_DEBUG) && defined(VIBE_RECORD)
|
||||
#include <tspdrvRecorder.c>
|
||||
#endif
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
/* Device name and version information */
|
||||
|
||||
/* DO NOT CHANGE - this is auto-generated */
|
||||
#define VERSION_STR " v3.4.55.8\n"
|
||||
|
||||
/* account extra space for future extra digits in version number */
|
||||
#define VERSION_STR_LEN 16
|
||||
|
||||
/* initialized in init_module */
|
||||
static char g_szDeviceName[ (VIBE_MAX_DEVICE_NAME_LENGTH
|
||||
+ VERSION_STR_LEN)* NUM_ACTUATORS];
|
||||
/* initialized in init_module */
|
||||
static size_t g_cchDeviceName;
|
||||
|
||||
static struct wake_lock vib_wake_lock;
|
||||
/* Flag indicating whether the driver is in use */
|
||||
static char g_bIsPlaying;
|
||||
|
||||
/* Buffer to store data sent to SPI */
|
||||
#define SPI_BUFFER_SIZE (NUM_ACTUATORS * \
|
||||
(VIBE_OUTPUT_SAMPLE_SIZE + SPI_HEADER_SIZE))
|
||||
static int g_bStopRequested;
|
||||
static actuator_samples_buffer g_SamplesBuffer[NUM_ACTUATORS] = {{0}};
|
||||
static char g_cWriteBuffer[SPI_BUFFER_SIZE];
|
||||
|
||||
|
||||
/* For QA purposes */
|
||||
#ifdef QA_TEST
|
||||
#define FORCE_LOG_BUFFER_SIZE 128
|
||||
#define TIME_INCREMENT 5
|
||||
static int g_nTime;
|
||||
static int g_nForceLogIndex;
|
||||
static VibeInt8 g_nForceLog[FORCE_LOG_BUFFER_SIZE];
|
||||
#endif
|
||||
|
||||
#if ((LINUX_VERSION_CODE & 0xFFFF00) < KERNEL_VERSION(2,6,0))
|
||||
#error Unsupported Kernel version
|
||||
#endif
|
||||
|
||||
/* Needs to be included after the global variables because it uses them */
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
#include "VibeOSKernelLinuxHRTime_isa1200.c"
|
||||
#else
|
||||
#include "VibeOSKernelLinuxTime_isa1200.c"
|
||||
#endif
|
||||
|
||||
static struct hrtimer timer;
|
||||
|
||||
static int max_timeout = 10000;
|
||||
static int vibrator_value = 0;
|
||||
static int vibrator_work;
|
||||
static int isa1200_enabled;
|
||||
|
||||
|
||||
struct pwm_device *vib_pwm;
|
||||
extern unsigned int get_hw_rev(void);
|
||||
static void _set_vibetonz_work(struct work_struct *unused);
|
||||
|
||||
static DECLARE_WORK(vibetonz_work, _set_vibetonz_work);
|
||||
struct vibrator_platform_data_isa1200 vibrator_drvdata;
|
||||
|
||||
#if defined (CONFIG_MACH_MATISSE3G_OPEN) || defined (CONFIG_SEC_MATISSELTE_COMMON) || defined (CONFIG_MACH_T10_3G_OPEN)
|
||||
static void haptic_power_onoff(int onoff)
|
||||
{
|
||||
int ret;
|
||||
static struct regulator *reg_l6;
|
||||
|
||||
if (!reg_l6) {
|
||||
reg_l6 = regulator_get(&vibrator_drvdata.client->dev,"vddo");
|
||||
if (IS_ERR(reg_l6)) {
|
||||
printk(KERN_ERR"could not get 8226_l6, rc = %ld\n",
|
||||
PTR_ERR(reg_l6));
|
||||
return;
|
||||
}
|
||||
ret = regulator_set_voltage(reg_l6, 1800000, 1800000);
|
||||
}
|
||||
|
||||
if (onoff) {
|
||||
ret = regulator_enable(reg_l6);
|
||||
if (ret) {
|
||||
printk(KERN_ERR"enable l6 failed, rc=%d\n", ret);
|
||||
return;
|
||||
}
|
||||
printk(KERN_DEBUG"haptic power_on is finished.\n");
|
||||
} else {
|
||||
if (regulator_is_enabled(reg_l6)) {
|
||||
ret = regulator_disable(reg_l6);
|
||||
if (ret) {
|
||||
printk(KERN_ERR"disable l6 failed, rc=%d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
printk(KERN_DEBUG"haptic power_off is finished.\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int set_vibetonz(int timeout)
|
||||
{
|
||||
int8_t strength;
|
||||
if(!timeout) {
|
||||
strength = 0;
|
||||
ImmVibeSPI_ForceOut_SetSamples(0,8,1,&strength);
|
||||
}
|
||||
else {
|
||||
DbgOut((KERN_INFO "tspdrv: ENABLE\n"));
|
||||
strength = 126;
|
||||
ImmVibeSPI_ForceOut_SetSamples(0,8,1,&strength);
|
||||
}
|
||||
|
||||
vibrator_value = timeout;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _set_vibetonz_work(struct work_struct *unused)
|
||||
{
|
||||
set_vibetonz(vibrator_work);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static enum hrtimer_restart vibetonz_timer_func(struct hrtimer *timer)
|
||||
{
|
||||
vibrator_work = 0;
|
||||
schedule_work(&vibetonz_work);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
static int get_time_for_vibetonz(struct timed_output_dev *dev)
|
||||
{
|
||||
int remaining;
|
||||
|
||||
if (hrtimer_active(&timer)) {
|
||||
ktime_t r = hrtimer_get_remaining(&timer);
|
||||
remaining = (int)ktime_to_ms(r);
|
||||
} else
|
||||
remaining = 0;
|
||||
|
||||
if (vibrator_value ==-1)
|
||||
remaining = -1;
|
||||
|
||||
return remaining;
|
||||
|
||||
}
|
||||
|
||||
static void enable_vibetonz_from_user(struct timed_output_dev *dev,int value)
|
||||
{
|
||||
printk("[VIBETONZ] %s : time = %d msec \n",__func__,value);
|
||||
hrtimer_cancel(&timer);
|
||||
/* set_vibetonz(value); */
|
||||
vibrator_work = value;
|
||||
schedule_work(&vibetonz_work);
|
||||
|
||||
|
||||
if (value > 0)
|
||||
{
|
||||
if (value > max_timeout)
|
||||
value = max_timeout;
|
||||
|
||||
hrtimer_start(&timer,ktime_set(value / 1000, (value % 1000) * 1000000),
|
||||
HRTIMER_MODE_REL);
|
||||
vibrator_value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct timed_output_dev timed_output_vt = {
|
||||
.name = "vibrator",
|
||||
.get_time = get_time_for_vibetonz,
|
||||
.enable = enable_vibetonz_from_user,
|
||||
};
|
||||
|
||||
static void vibetonz_start(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
timer.function = vibetonz_timer_func;
|
||||
|
||||
ret = timed_output_dev_register(&timed_output_vt);
|
||||
if(ret)
|
||||
DbgOut((KERN_ERR "[VIBETONZ] timed_output_dev_register is fail \n"));
|
||||
}
|
||||
|
||||
|
||||
/* File IO */
|
||||
static int open(struct inode *inode, struct file *file);
|
||||
static int release(struct inode *inode, struct file *file);
|
||||
static ssize_t read(struct file *file, char *buf, size_t count, loff_t *ppos);
|
||||
static ssize_t write(struct file *file, const char *buf, size_t count,
|
||||
loff_t *ppos);
|
||||
static long unlocked_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = read,
|
||||
.write = write,
|
||||
.unlocked_ioctl = unlocked_ioctl,
|
||||
.open = open,
|
||||
.release = release,
|
||||
.llseek = default_llseek
|
||||
};
|
||||
|
||||
static struct miscdevice miscdev = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = MODULE_NAME,
|
||||
.fops = &fops
|
||||
};
|
||||
|
||||
/* Module info */
|
||||
|
||||
int vibrator_write_register(u8 addr, u8 w_data)
|
||||
{
|
||||
|
||||
if (i2c_smbus_write_byte_data(vibrator_drvdata.client, addr, w_data) < 0) {
|
||||
pr_err("%s: Failed to write addr=[0x%x], data=[0x%x]\n",
|
||||
__func__, addr, w_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int isa1200_parse_dt(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
vibrator_drvdata.motor_en = of_get_named_gpio(np, "isa1200,motor_en",0);
|
||||
vibrator_drvdata.vib_clk = of_get_named_gpio(np, "isa1200,vib_clk",0);
|
||||
#if defined(CONFIG_MACH_T10_3G_OPEN)
|
||||
gpio_tlmm_config(GPIO_CFG(vibrator_drvdata.motor_en, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),GPIO_CFG_DISABLE);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int __devinit isa1200_vibrator_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
||||
int nRet, i,error; /* initialized below */
|
||||
DbgOut((KERN_INFO "tspdrv: Probe called.\n"));
|
||||
motor_min_strength = g_nlra_gp_clk_n*MOTOR_MIN_STRENGTH/100;
|
||||
|
||||
vibrator_drvdata.client = client;
|
||||
if(!(client->dev.of_node)){
|
||||
DbgOut(KERN_ERR "tspdrv: tspdrv probe failed, DT is NULL");
|
||||
return -ENODEV;
|
||||
}
|
||||
error = isa1200_parse_dt(&client->dev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if( gpio_request(vibrator_drvdata.motor_en, "MOTOR_EN") < 0)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpio_direction_output(vibrator_drvdata.motor_en, 0);
|
||||
gpio_export(vibrator_drvdata.motor_en, 0);
|
||||
|
||||
virt_mmss_gp1_base = ioremap(MSM_MMSS_GP1_BASE,0x28);
|
||||
if (!virt_mmss_gp1_base)
|
||||
panic("tspdrv : Unable to ioremap MSM_MMSS_GP1 memory!");
|
||||
|
||||
#if defined(CONFIG_MACH_MATISSE3G_OPEN) || defined (CONFIG_SEC_MATISSELTE_COMMON) || defined (CONFIG_MACH_T10_3G_OPEN)
|
||||
vibrator_drvdata.power_onoff = haptic_power_onoff;
|
||||
#endif
|
||||
nRet = misc_register(&miscdev);
|
||||
if (nRet)
|
||||
{
|
||||
DbgOut((KERN_ERR "tspdrv: misc_register failed.\n"));
|
||||
iounmap(virt_mmss_gp1_base);
|
||||
return nRet;
|
||||
}
|
||||
|
||||
|
||||
DbgRecorderInit(());
|
||||
ImmVibeSPI_ForceOut_Initialize();
|
||||
VibeOSKernelLinuxInitTimer();
|
||||
|
||||
isa1200_enabled = 1;
|
||||
g_cchDeviceName = 0;
|
||||
for (i = 0; i < NUM_ACTUATORS; i++) {
|
||||
char *szName = g_szDeviceName + g_cchDeviceName;
|
||||
ImmVibeSPI_Device_GetName(i, szName,
|
||||
VIBE_MAX_DEVICE_NAME_LENGTH);
|
||||
|
||||
/* Append version information and get buffer length */
|
||||
strcat(szName, VERSION_STR);
|
||||
g_cchDeviceName += strlen(szName);
|
||||
|
||||
g_SamplesBuffer[i].nIndexPlayingBuffer = -1; /* Not playing */
|
||||
g_SamplesBuffer[i].actuatorSamples[0].nBufferSize = 0;
|
||||
g_SamplesBuffer[i].actuatorSamples[1].nBufferSize = 0;
|
||||
}
|
||||
|
||||
wake_lock_init(&vib_wake_lock, WAKE_LOCK_SUSPEND, "vib_present");
|
||||
vibetonz_start();
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int __devexit isa1200_remove(struct i2c_client *client)
|
||||
{
|
||||
DbgOut((KERN_INFO "tspdrv: isa1200_remove_module.\n"));
|
||||
iounmap(virt_mmss_gp1_base);
|
||||
DbgRecorderTerminate(());
|
||||
VibeOSKernelLinuxTerminateTimer();
|
||||
ImmVibeSPI_ForceOut_Terminate();
|
||||
wake_lock_destroy(&vib_wake_lock);
|
||||
misc_deregister(&miscdev);
|
||||
gpio_free(vibrator_drvdata.motor_en);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int open(struct inode *inode, struct file *file)
|
||||
{
|
||||
DbgOut((KERN_INFO "tspdrv: open.\n"));
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int release(struct inode *inode, struct file *file)
|
||||
{
|
||||
DbgOut((KERN_INFO "tspdrv: release.\n"));
|
||||
|
||||
/*
|
||||
** Reset force and stop timer when the driver is closed, to make sure
|
||||
** no dangling semaphore remains in the system, especially when the
|
||||
** driver is run outside of immvibed for testing purposes.
|
||||
*/
|
||||
VibeOSKernelLinuxStopTimer();
|
||||
|
||||
/*
|
||||
** Clear the variable used to store the magic number to prevent
|
||||
** unauthorized caller to write data. TouchSense service is the only
|
||||
** valid caller.
|
||||
*/
|
||||
file->private_data = (void*)NULL;
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t read(struct file *file, char *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
const size_t nBufSize = (g_cchDeviceName > (size_t)(*ppos)) ?
|
||||
min(count, g_cchDeviceName - (size_t)(*ppos)) : 0;
|
||||
|
||||
/* End of buffer, exit */
|
||||
if (0 == nBufSize)
|
||||
return 0;
|
||||
|
||||
if (0 != copy_to_user(buf, g_szDeviceName + (*ppos), nBufSize)) {
|
||||
/* Failed to copy all the data, exit */
|
||||
DbgOut((KERN_ERR "tspdrv: copy_to_user failed.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update file position and return copied buffer size */
|
||||
*ppos += nBufSize;
|
||||
return nBufSize;
|
||||
}
|
||||
|
||||
static ssize_t write(struct file *file, const char *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
*ppos = 0; /* file position not used, always set to 0 */
|
||||
|
||||
/*
|
||||
** Prevent unauthorized caller to write data.
|
||||
** TouchSense service is the only valid caller.
|
||||
*/
|
||||
if (file->private_data != (void *)TSPDRV_MAGIC_NUMBER) {
|
||||
DbgOut((KERN_ERR "tspdrv: unauthorized write.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VIBRATOR_UPDATE
|
||||
/* Check buffer size */
|
||||
if ((count < SPI_HEADER_SIZE) || (count > SPI_BUFFER_SIZE)) {
|
||||
DbgOut((KERN_ERR "tspdrv: invalid write buffer size.\n"));
|
||||
return 0;
|
||||
}
|
||||
if (count == SPI_HEADER_SIZE)
|
||||
g_bOutputDataBufferEmpty = 1;
|
||||
else
|
||||
g_bOutputDataBufferEmpty = 0;
|
||||
|
||||
#else
|
||||
if ((count <= SPI_HEADER_SIZE) || (count > SPI_BUFFER_SIZE)) {
|
||||
DbgOut((KERN_ERR "tspdrv: invalid write buffer size.\n"));
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
/* Copy immediately the input buffer */
|
||||
if (0 != copy_from_user(g_cWriteBuffer, buf, count)) {
|
||||
/* Failed to copy all the data, exit */
|
||||
DbgOut((KERN_ERR "tspdrv: copy_from_user failed.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
while (i < count) {
|
||||
int nIndexFreeBuffer; /* initialized below */
|
||||
|
||||
samples_buffer* pInputBuffer = (samples_buffer *)
|
||||
(&g_cWriteBuffer[i]);
|
||||
|
||||
#ifdef CONFIG_VIBRATOR_UPDATE
|
||||
if ((i + SPI_HEADER_SIZE) > count) {
|
||||
#else
|
||||
if ((i + SPI_HEADER_SIZE) >= count) {
|
||||
#endif
|
||||
/*
|
||||
** Index is about to go beyond the buffer size.
|
||||
** (Should never happen).
|
||||
*/
|
||||
DbgOut((KERN_EMERG "tspdrv: invalid buffer index.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check bit depth */
|
||||
if (8 != pInputBuffer->nBitDepth)
|
||||
DbgOut((KERN_WARNING
|
||||
"tspdrv: invalid bit depth."
|
||||
"Use default value (8)\n"));
|
||||
|
||||
/* The above code not valid if SPI header size is not 3 */
|
||||
#if (SPI_HEADER_SIZE != 3)
|
||||
#error "SPI_HEADER_SIZE expected to be 3"
|
||||
#endif
|
||||
|
||||
/* Check buffer size */
|
||||
if ((i + SPI_HEADER_SIZE + pInputBuffer->nBufferSize) > count) {
|
||||
/*
|
||||
** Index is about to go beyond the buffer size.
|
||||
** (Should never happen).
|
||||
*/
|
||||
DbgOut((KERN_EMERG "tspdrv: invalid data size.\n"));
|
||||
return 0;
|
||||
}
|
||||
/* Check actuator index */
|
||||
if (NUM_ACTUATORS <= pInputBuffer->nActuatorIndex) {
|
||||
DbgOut((KERN_ERR "tspdrv: invalid actuator index.\n"));
|
||||
i += (SPI_HEADER_SIZE + pInputBuffer->nBufferSize);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (0 == g_SamplesBuffer[pInputBuffer->nActuatorIndex].
|
||||
actuatorSamples[0].nBufferSize) {
|
||||
nIndexFreeBuffer = 0;
|
||||
} else if (0 == g_SamplesBuffer[pInputBuffer->nActuatorIndex].
|
||||
actuatorSamples[1].nBufferSize) {
|
||||
nIndexFreeBuffer = 1;
|
||||
} else {
|
||||
/* No room to store new samples */
|
||||
DbgOut((KERN_ERR
|
||||
"tspdrv: no room to store new samples.\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Store the data in the free buffer of the given actuator */
|
||||
memcpy(&(g_SamplesBuffer[pInputBuffer->nActuatorIndex].
|
||||
actuatorSamples[nIndexFreeBuffer]), &g_cWriteBuffer[i],
|
||||
(SPI_HEADER_SIZE + pInputBuffer->nBufferSize));
|
||||
|
||||
/* if the no buffer is playing, prepare to play
|
||||
* g_SamplesBuffer[pInputBuffer->nActuatorIndex].
|
||||
* actuatorSamples[nIndexFreeBuffer] */
|
||||
if (-1 == g_SamplesBuffer[pInputBuffer->
|
||||
nActuatorIndex].nIndexPlayingBuffer) {
|
||||
g_SamplesBuffer[pInputBuffer->nActuatorIndex].
|
||||
nIndexPlayingBuffer = nIndexFreeBuffer;
|
||||
g_SamplesBuffer[pInputBuffer->nActuatorIndex].
|
||||
nIndexOutputValue = 0;
|
||||
}
|
||||
|
||||
/* Call SPI */
|
||||
ImmVibeSPI_ForceOut_SetSamples(pInputBuffer->nActuatorIndex, pInputBuffer->nBitDepth, pInputBuffer->nBufferSize, &(g_SamplesBuffer[pInputBuffer->nActuatorIndex].actuatorSamples[nIndexFreeBuffer].dataBuffer[0]));
|
||||
|
||||
/* Increment buffer index */
|
||||
i += (SPI_HEADER_SIZE + pInputBuffer->nBufferSize);
|
||||
}
|
||||
|
||||
#ifdef QA_TEST
|
||||
g_nForceLog[g_nForceLogIndex++] = g_cSPIBuffer[0];
|
||||
if (g_nForceLogIndex >= FORCE_LOG_BUFFER_SIZE) {
|
||||
for (i = 0; i < FORCE_LOG_BUFFER_SIZE; i++) {
|
||||
printk(KERN_DEBUG "<6>%d\t%d\n",g_nTime, g_nForceLog[i]);
|
||||
g_nTime += TIME_INCREMENT;
|
||||
}
|
||||
g_nForceLogIndex = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Start the timer after receiving new output force */
|
||||
g_bIsPlaying = true;
|
||||
VibeOSKernelLinuxStartTimer();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static long unlocked_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
#ifdef QA_TEST
|
||||
int i;
|
||||
#endif
|
||||
|
||||
DbgOut((KERN_INFO "tspdrv: ioctl cmd[0x%x].\n", cmd));
|
||||
|
||||
switch (cmd) {
|
||||
case TSPDRV_STOP_KERNEL_TIMER:
|
||||
/*
|
||||
* As we send one sample ahead of time,
|
||||
* we need to finish playing the last sample
|
||||
* before stopping the timer. So we just set a flag here.
|
||||
*/
|
||||
if (g_bIsPlaying)
|
||||
g_bStopRequested = true;
|
||||
|
||||
#ifdef VIBEOSKERNELPROCESSDATA
|
||||
/* Last data processing to disable amp and stop timer */
|
||||
VibeOSKernelProcessData(NULL);
|
||||
#endif
|
||||
|
||||
#ifdef QA_TEST
|
||||
if (g_nForceLogIndex) {
|
||||
for (i = 0; i < g_nForceLogIndex; i++) {
|
||||
printk(KERN_DEBUG "<6>%d\t%d\n",g_nTime, g_nForceLog[i]);
|
||||
g_nTime += TIME_INCREMENT;
|
||||
}
|
||||
}
|
||||
g_nTime = 0;
|
||||
g_nForceLogIndex = 0;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case TSPDRV_MAGIC_NUMBER:
|
||||
#ifdef CONFIG_VIBRATOR_UPDATE
|
||||
case TSPDRV_SET_MAGIC_NUMBER:
|
||||
#endif
|
||||
file->private_data = (void*)TSPDRV_MAGIC_NUMBER;
|
||||
break;
|
||||
|
||||
case TSPDRV_ENABLE_AMP:
|
||||
wake_lock(&vib_wake_lock);
|
||||
vibe_set_pwm_freq(0);
|
||||
vibe_pwm_onoff(1);
|
||||
ImmVibeSPI_ForceOut_AmpEnable(arg);
|
||||
DbgRecorderReset((arg));
|
||||
DbgRecord((arg,";------- TSPDRV_ENABLE_AMP ---------\n"));
|
||||
break;
|
||||
|
||||
case TSPDRV_DISABLE_AMP:
|
||||
/* Small fix for now to handle proper combination of
|
||||
* TSPDRV_STOP_KERNEL_TIMER and TSPDRV_DISABLE_AMP together
|
||||
* If a stop was requested, ignore the request as the amp
|
||||
* will be disabled by the timer proc when it's ready
|
||||
*/
|
||||
#ifdef CONFIG_VIBRATOR_UPDATE
|
||||
g_bStopRequested = true;
|
||||
/* Last data processing to disable amp and stop timer */
|
||||
VibeOSKernelProcessData(NULL);
|
||||
g_bIsPlaying = false;
|
||||
#else
|
||||
if (!g_bStopRequested)
|
||||
ImmVibeSPI_ForceOut_AmpDisable(arg);
|
||||
#endif
|
||||
wake_unlock(&vib_wake_lock);
|
||||
break;
|
||||
|
||||
case TSPDRV_GET_NUM_ACTUATORS:
|
||||
return NUM_ACTUATORS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int isa1200_vibrator_suspend(struct i2c_client *client,pm_message_t mesg)
|
||||
{
|
||||
|
||||
int ret = 0;
|
||||
|
||||
if (g_bIsPlaying){
|
||||
ret = -EBUSY;
|
||||
}
|
||||
else{
|
||||
DbgOut((KERN_INFO "tspdrv: suspend.\n"));
|
||||
if(isa1200_enabled){
|
||||
vibrator_write_register(0x30, 0x08);
|
||||
gpio_set_value(vibrator_drvdata.motor_en, VIBRATION_OFF);
|
||||
isa1200_enabled = 0;
|
||||
}
|
||||
DbgOut((KERN_ERR "[VIBTONZ] isa1200_early_suspend \n"));
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int isa1200_vibrator_resume(struct i2c_client *client)
|
||||
{
|
||||
|
||||
DbgOut(KERN_DEBUG "[VIBTONZ] isa1200_vibrator_resume \n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id isa1200_vibrator_device_id[] = {
|
||||
{"isa1200_vibrator", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, isa1200_vibrator_device_id);
|
||||
|
||||
static struct of_device_id isa1200_vibrator_table[] = {
|
||||
|
||||
{ .compatible = "isa1200_vibrator,vibrator",},
|
||||
{ },
|
||||
};
|
||||
|
||||
|
||||
static struct i2c_driver isa1200_vibrator_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "isa1200_vibrator",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = isa1200_vibrator_table,
|
||||
},
|
||||
.probe = isa1200_vibrator_i2c_probe,
|
||||
.id_table = isa1200_vibrator_device_id,
|
||||
.suspend = isa1200_vibrator_suspend,
|
||||
.resume = isa1200_vibrator_resume,
|
||||
.remove = __devexit_p(isa1200_remove),
|
||||
};
|
||||
|
||||
static int __init isa1200_vibrator_init(void)
|
||||
{
|
||||
return(i2c_add_driver(&isa1200_vibrator_i2c_driver));
|
||||
}
|
||||
|
||||
static void __exit isa1200_vibrator_exit(void)
|
||||
{
|
||||
i2c_del_driver(&isa1200_vibrator_i2c_driver);
|
||||
}
|
||||
|
||||
module_init(isa1200_vibrator_init);
|
||||
module_exit(isa1200_vibrator_exit);
|
||||
|
||||
MODULE_AUTHOR("Immersion Corporation");
|
||||
MODULE_DESCRIPTION("TouchSense Kernel Module");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue