drivers: motor: import vibrator driver from CRJ1 package

This commit is contained in:
xXPR0T0TYPEXx 2020-01-01 22:56:29 +01:00 committed by Francescodario Cuzzocrea
parent 2897e8b7db
commit 60780e3e6b
32 changed files with 1574 additions and 6582 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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/

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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.

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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,

451
drivers/motor/isa1200_vibrator.c Executable file
View File

@ -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");

View File

@ -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

View File

@ -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");

View File

@ -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,
},
};

View File

@ -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,
},
};

View File

@ -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,
},
};

View File

@ -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,
},
};

908
drivers/motor/ss_vibrator.c Executable file
View File

@ -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");

View File

@ -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

View File

@ -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;
}

View File

@ -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");

View File

@ -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 */

View File

@ -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");