aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/motor
diff options
context:
space:
mode:
authorcodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
committercodeworkx <daniel.hillenbrand@codeworkx.de>2012-06-02 13:09:29 +0200
commitc6da2cfeb05178a11c6d062a06f8078150ee492f (patch)
treef3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/motor
parentc6d7c4dbff353eac7919342ae6b3299a378160a6 (diff)
downloadkernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz
kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2
samsung update 1
Diffstat (limited to 'drivers/motor')
-rw-r--r--drivers/motor/ImmVibeSPI.c268
-rw-r--r--drivers/motor/ImmVibeSPI_isa1200.c561
-rw-r--r--drivers/motor/Kconfig32
-rw-r--r--drivers/motor/Makefile10
-rw-r--r--drivers/motor/VibeOSKernelLinuxHRTime.c233
-rw-r--r--drivers/motor/isa1200_vibrator.c386
-rw-r--r--drivers/motor/max77693_haptic.c388
-rw-r--r--drivers/motor/max8997_vibrator.c336
-rw-r--r--drivers/motor/tspdrv.c587
-rw-r--r--drivers/motor/tspdrv.h110
10 files changed, 2911 insertions, 0 deletions
diff --git a/drivers/motor/ImmVibeSPI.c b/drivers/motor/ImmVibeSPI.c
new file mode 100644
index 0000000..dea4ea4
--- /dev/null
+++ b/drivers/motor/ImmVibeSPI.c
@@ -0,0 +1,268 @@
+/*
+** =========================================================================
+** 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/pwm.h>
+#include <plat/gpio-cfg.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/mfd/max8997.h>
+#include <linux/mfd/max8997-private.h>
+
+#include "tspdrv.h"
+
+#ifdef IMMVIBESPIAPI
+#undef IMMVIBESPIAPI
+#endif
+#define IMMVIBESPIAPI static
+
+/*
+** This SPI supports only one actuator.
+*/
+#define NUM_ACTUATORS 1
+
+#define PWM_DUTY_MAX 579 /* 13MHz / (579 + 1) = 22.4kHz */
+
+static bool g_bAmpEnabled;
+
+extern void vibtonz_en(bool en);
+extern void vibtonz_pwm(int nForce);
+
+/*
+** Called to disable amp (disable output force)
+*/
+IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpDisable(VibeUInt8 nActuatorIndex)
+{
+ /*printk(KERN_ERR"[VIBRATOR] ImmVibeSPI_ForceOut_AmpDisable is called\n");*/
+#if 0
+ if (g_bAmpEnabled) {
+ DbgOut((KERN_DEBUG "ImmVibeSPI_ForceOut_AmpDisable.\n"));
+
+ g_bAmpEnabled = false;
+
+ /* Disable amp */
+
+
+ /* Disable PWM CLK */
+
+ }
+#endif
+
+ if (g_bAmpEnabled) {
+
+ g_bAmpEnabled = false;
+#if !defined(CONFIG_MACH_P4NOTE)
+ vibtonz_pwm(0);
+ vibtonz_en(false);
+#endif
+ if (regulator_hapticmotor_enabled == 1) {
+ regulator_hapticmotor_enabled = 0;
+
+ printk(KERN_DEBUG "Motor:tspdrv: %s (%d)\n", __func__,
+ regulator_hapticmotor_enabled);
+
+ }
+ }
+
+ return VIBE_S_SUCCESS;
+}
+
+/*
+** Called to enable amp (enable output force)
+*/
+IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpEnable(VibeUInt8 nActuatorIndex)
+{
+ /*printk(KERN_ERR"[VIBRATOR]ImmVibeSPI_ForceOut_AmpEnable is called\n");*/
+#if 0
+ if (!g_bAmpEnabled) {
+ DbgOut((KERN_DEBUG "ImmVibeSPI_ForceOut_AmpEnable.\n"));
+
+ g_bAmpEnabled = true;
+
+ /* Generate PWM CLK with proper frequency(ex. 22400Hz) and 50% duty cycle.*/
+
+
+ /* Enable amp */
+
+ }
+#endif
+
+ if (!g_bAmpEnabled) {
+#if !defined(CONFIG_MACH_P4NOTE)
+ vibtonz_en(true);
+#endif
+
+ g_bAmpEnabled = true;
+ regulator_hapticmotor_enabled = 1;
+
+ printk(KERN_DEBUG "Motor:tspdrv: %s (%d)\n", __func__,
+ regulator_hapticmotor_enabled);
+
+ }
+
+ return VIBE_S_SUCCESS;
+}
+
+/*
+** Called at initialization time to set PWM freq, disable amp, etc...
+*/
+IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_Initialize(void)
+{
+ /*printk(KERN_ERR"[VIBRATOR]ImmVibeSPI_ForceOut_Initialize is called\n");*/
+ DbgOut((KERN_DEBUG "ImmVibeSPI_ForceOut_Initialize.\n"));
+
+ g_bAmpEnabled = true; /* to force ImmVibeSPI_ForceOut_AmpDisable disabling the amp */
+
+ /*
+ ** 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);
+
+ regulator_hapticmotor_enabled = 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;
+}
+
+/*
+** Called by the real-time loop to set PWM duty cycle
+*/
+IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_SetSamples(VibeUInt8 nActuatorIndex,
+ VibeUInt16 nOutputSignalBitDepth,
+ VibeUInt16 nBufferSizeInBytes,
+ VibeInt8 * pForceOutputBuffer)
+{
+#if 0
+ VibeInt8 nForce;
+
+ 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 = ((VibeInt16 *)pForceOutputBuffer)[0] >> 8;
+ break;
+ default:
+ /* Unexpected bit depth */
+ return VIBE_E_FAIL;
+ }
+
+ if (nForce == 0)
+ /* Set 50% duty cycle or disable amp */
+ else
+ /* Map force from [-127, 127] to [0, PWM_DUTY_MAX] */
+
+
+#endif
+
+ VibeInt8 nForce;
+
+ 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 = ((VibeInt16 *)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);
+ } else {
+ /* Map force from [-127, 127] to [0, PWM_DUTY_MAX] */
+ ImmVibeSPI_ForceOut_AmpEnable(0);
+#if !defined(CONFIG_MACH_P4NOTE)
+ vibtonz_pwm(nForce);
+#endif
+ }
+
+ return VIBE_S_SUCCESS;
+}
+
+#if 0 /* Unused */
+/*
+** 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;
+}
+
diff --git a/drivers/motor/ImmVibeSPI_isa1200.c b/drivers/motor/ImmVibeSPI_isa1200.c
new file mode 100644
index 0000000..42c36b5
--- /dev/null
+++ b/drivers/motor/ImmVibeSPI_isa1200.c
@@ -0,0 +1,561 @@
+/*
+** =========================================================================
+** 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/isa1200_vibrator.h>
+
+/*
+** This SPI supports only one actuator.
+*/
+#define NUM_ACTUATORS 1
+
+#define ISA1200_I2C_ADDRESS 0x90
+#define SCTRL (0)
+#define HCTRL0 (0x30)
+#define HCTRL1 (0x31)
+#define HCTRL2 (0x32)
+#define HCTRL3 (0x33)
+#define HCTRL4 (0x34)
+#define HCTRL5 (0x35)
+#define HCTRL6 (0x36)
+#define HCTRL7 (0x37)
+#define HCTRL8 (0x38)
+#define HCTRL9 (0x39)
+#define HCTRLA (0x3A)
+#define HCTRLB (0x3B)
+#define HCTRLC (0x3C)
+#define HCTRLD (0x3D)
+
+#define LDO_VOLTAGE_23V 0x08
+#define LDO_VOLTAGE_24V 0x09
+#define LDO_VOLTAGE_25V 0x0A
+#define LDO_VOLTAGE_26V 0x0B
+#define LDO_VOLTAGE_27V 0x0C
+#define LDO_VOLTAGE_28V 0x0D
+#define LDO_VOLTAGE_29V 0x0E
+#define LDO_VOLTAGE_30V 0x0F
+#define LDO_VOLTAGE_31V 0x00
+#define LDO_VOLTAGE_32V 0x01
+#define LDO_VOLTAGE_33V 0x02
+#define LDO_VOLTAGE_34V 0x03
+#define LDO_VOLTAGE_35V 0x04
+#define LDO_VOLTAGE_36V 0x05
+#define LDO_VOLTAGE_37V 0x06
+#define LDO_VOLTAGE_38V 0x07
+
+static bool g_bAmpEnabled;
+
+/* Variable defined to allow for tuning of the handset. */
+/* For temporary section for Tuning Work */
+#if 0
+#define VIBETONZ_TUNING
+#define ISA1200_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 ISA1200_GEN_MODE
+#define ISA1200_PWM_MODE
+#if 0
+#define ISA1200_PWM_256DIV_MODE
+#else
+#define ISA1200_PWM_128DIV_MODE
+#endif
+#endif
+
+/* Actuator selection */
+#ifndef MOTOR_TYPE_LRA
+#define MOTOR_TYPE_ERM
+#endif
+
+#define ISA1200_HEN_ENABLE
+#define RETRY_CNT 10
+
+#define SYS_API_LEN_HIGH vibtonz_chip_enable(true)
+#define SYS_API_LEN_LOW vibtonz_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) \
+ vibtonz_i2c_write(_addr, _dataLength, _data)
+#define SLEEP(_us_time)
+
+#define PWM_CLK_ENABLE vibtonz_clk_enable(true)
+#define PWM_CLK_DISABLE vibtonz_clk_enable(false)
+
+#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(ratio) vibtonz_clk_config(ratio)
+
+#ifdef ISA1200_GEN_MODE
+#define PWM_PLLDIV_DEFAULT 0x02
+#define PWM_FREQ_DEFAULT 0x00
+#define PWM_PERIOD_DEFAULT 0x74
+#define PWM_DUTY_DEFAULT 0x3a
+VibeUInt32 g_nPWM_PLLDiv = PWM_PLLDIV_DEFAULT;
+VibeUInt32 g_nPWM_Freq = PWM_FREQ_DEFAULT;
+VibeUInt32 g_nPWM_Period = PWM_PERIOD_DEFAULT;
+VibeUInt32 g_nPWM_Duty = PWM_DUTY_DEFAULT;
+#else
+#ifdef ISA1200_PWM_256DIV_MODE
+#define PWM_PERIOD_DEFAULT 44800
+#else
+#define PWM_PERIOD_DEFAULT 22400
+#endif
+VibeUInt32 g_nPWM_Freq = PWM_PERIOD_DEFAULT;
+#endif
+
+#define LDO_VOLTAGE_DEFAULT LDO_VOLTAGE_30V
+VibeUInt32 g_nLDO_Voltage = LDO_VOLTAGE_DEFAULT;
+
+#define DEVICE_NAME "Generic"
+
+/*
+** Called to disable amp (disable output force)
+*/
+IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpDisable(VibeUInt8 nActuatorIndex)
+{
+ int cnt = 0;
+ unsigned char I2C_data[2];
+ int ret = VIBE_S_SUCCESS;
+
+ if (g_bAmpEnabled)
+ {
+ DbgOut((KERN_DEBUG "ImmVibeSPI_ForceOut_AmpDisable.\n"));
+
+ g_bAmpEnabled = false;
+
+
+#ifdef ISA1200_HEN_ENABLE
+ SYS_API_HEN_LOW;
+#endif
+
+ I2C_data[1] = 0x00; // Haptic drive disable
+ I2C_data[0]= HCTRL0;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_AmpDisable] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", HCTRL0, ret);
+
+ PWM_CLK_DISABLE;
+ }
+
+ return VIBE_S_SUCCESS;
+}
+
+/*
+** Called to enable amp (enable output force)
+*/
+IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_AmpEnable(VibeUInt8 nActuatorIndex)
+{
+ int cnt = 0;
+ unsigned char I2C_data[2];
+ int ret = VIBE_S_SUCCESS;
+#ifdef VIBETONZ_TUNING
+ VibeUInt32 nPWM_PLLDiv_KernelParam = 0;
+ VibeUInt32 nPWM_Freq_KernelParam = 0;
+ VibeUInt32 nPWM_Period_KernelParam = 0;
+ VibeUInt32 nPWM_Duty_KernelParam = 0;
+ VibeUInt32 nPWM_Update_KernelParam = 0;
+#endif
+
+ if (!g_bAmpEnabled)
+ {
+ DbgOut((KERN_DEBUG "ImmVibeSPI_ForceOut_AmpEnable.\n"));
+ g_bAmpEnabled = true;
+
+#ifdef VIBETONZ_TUNING /* For Resonant Frequency Test */
+ ImmVibeGetDeviceKernelParameter(0, 85, &nPWM_PLLDiv_KernelParam); /* use for PLL variable */
+ ImmVibeGetDeviceKernelParameter(0, 86, &nPWM_Freq_KernelParam); /* use for Freq variable */
+ ImmVibeGetDeviceKernelParameter(0, 87, &nPWM_Period_KernelParam); /* use for Period variable */
+ ImmVibeGetDeviceKernelParameter(0, 88, &nPWM_Duty_KernelParam); /* use for Duty variable */
+ ImmVibeGetDeviceKernelParameter(0, 89, &nPWM_Update_KernelParam); /* use for Update variable */
+
+ /* Update current Period and Duty values if those from the Kernel Parameters table are acceptables (value != 0) */
+#ifdef ISA1200_GEN_MODE
+ if(nPWM_Update_KernelParam)
+ {
+ g_nPWM_PLLDiv = nPWM_PLLDiv_KernelParam;
+ g_nPWM_Freq = nPWM_Freq_KernelParam;
+ g_nPWM_Period = nPWM_Period_KernelParam;
+ g_nPWM_Duty = nPWM_Duty_KernelParam;
+
+#ifdef MOTOR_TYPE_LRA
+ I2C_data[1] = 0xC0 ; // EXT clock + DAC inversion + LRA
+#else
+ I2C_data[1] = 0xE0; // EXT clock + DAC inversion + ERM
+#endif
+ I2C_data[0]= HCTRL1;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_AmpEnable] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+ I2C_data[1] = 0x00 ; // Disable Software Reset
+ I2C_data[0]= HCTRL2;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_AmpEnable] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+ I2C_data[1] = 0x03 + ( g_nPWM_PLLDiv<<4); // PLLLDIV
+ I2C_data[0]= HCTRL3;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_AmpEnable] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+ I2C_data[1] = g_nPWM_Freq; // PWM control
+ I2C_data[0]= HCTRL4;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_AmpEnable] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+ I2C_data[1] = g_nPWM_Duty; // PWM High Duty
+ I2C_data[0]= HCTRL5;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_AmpEnable] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+ I2C_data[1] = g_nPWM_Period; // PWM Period
+ I2C_data[0]= HCTRL6;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_AmpEnable] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+ }
+#else
+ if(nPWM_Update_KernelParam)
+ {
+ g_nPWM_Freq = nPWM_Freq_KernelParam;
+ }
+ SYS_API_SET_PWM_FREQ(g_nPWM_Freq); // 22.4Khz
+ SYS_API_SET_PWM_DUTY(500); // 50% Duty Cycle, 1000 ~ 0
+#endif
+#endif /* VIBETONZ_TUNING */
+
+ PWM_CLK_ENABLE;
+
+#ifdef ISA1200_HEN_ENABLE
+ SYS_API_HEN_HIGH;
+#endif
+
+#ifdef ISA1200_GEN_MODE
+ I2C_data[1] = 0x91; //Haptic Enable + PWM generation mode
+#else
+#ifdef ISA1200_PWM_256DIV_MODE
+ I2C_data[1] = 0x89; // Haptic Drive Disable + PWM Input mode + 44.8Khz mode
+#else
+ I2C_data[1] = 0x88; // Haptic Drive Disable + PWM Input mode + 22.4Khz mode
+#endif
+#endif
+ I2C_data[0]= HCTRL0;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_AmpEnable] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+ }
+
+ return VIBE_S_SUCCESS;
+}
+
+/*
+** Called at initialization time to set PWM frequencies, disable amp, etc...
+*/
+IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_Initialize(void)
+{
+ int cnt = 0;
+ unsigned char I2C_data[2];
+ int ret = VIBE_S_SUCCESS;
+
+ DbgOut((KERN_DEBUG "ImmVibeSPI_ForceOut_Initialize.\n"));
+
+ SYS_API_VDDP_ON;
+ SYS_API_LEN_HIGH;
+
+ SLEEP(200 /*us*/);
+
+ I2C_data[1] = g_nLDO_Voltage; // LDO Voltage
+ I2C_data[0]= SCTRL;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Initialize] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+#ifdef ISA1200_GEN_MODE
+ I2C_data[1] = 0x11; //Haptic Drive Disable + PWM generation mode + 44.8Khz mode
+ I2C_data[0]= HCTRL0;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Initialize] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+#ifdef MOTOR_TYPE_LRA
+ I2C_data[1] = 0xC0 ; // EXT clock + DAC inversion + LRA
+#else
+ I2C_data[1] = 0xE0; // EXT clock + DAC inversion + ERM
+#endif
+ I2C_data[0]= HCTRL1;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Initialize] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+ I2C_data[1] = 0x00 ; // Disable Software Reset
+ I2C_data[0]= HCTRL2;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Initialize] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+ I2C_data[1] = 0x03 + ( g_nPWM_PLLDiv<<4); // PLLLDIV
+ I2C_data[0]= HCTRL3;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Initialize] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+ I2C_data[1] = g_nPWM_Freq; // PWM control
+ I2C_data[0]= HCTRL4;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Initialize] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+ I2C_data[1] = g_nPWM_Duty; // PWM High Duty
+ I2C_data[0]= HCTRL5;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Initialize] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+ I2C_data[1] = g_nPWM_Period; // PWM Period
+ I2C_data[0]= HCTRL6;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Initialize] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+#else // PWM Input Mode
+
+#ifdef ISA1200_PWM_256DIV_MODE
+ I2C_data[1] = 0x09; // Haptic Drive Disable + PWM Input mode + 44.8Khz mode
+#else
+ I2C_data[1] = 0x08; // Haptic Drive Disable + PWM Input mode + 22.4Khz mode
+#endif
+ I2C_data[0]= HCTRL0;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Initialize] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+#ifdef MOTOR_TYPE_LRA
+ I2C_data[1] = 0x43; // EXT clock + DAC inversion + LRA
+#else
+ I2C_data[1] = 0x63; // EXT clock + DAC inversion + ERM
+#endif
+ I2C_data[0]= HCTRL1;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Initialize] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+ I2C_data[1] = 0x00; // Disable Software Reset
+ I2C_data[0]= HCTRL2;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Initialize] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+
+ I2C_data[1] = 0x00; // Default
+ I2C_data[0]= HCTRL4;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Initialize] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+#endif
+ 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"));
+ SYS_API_LEN_LOW;
+#ifdef ISA1200_HEN_ENABLE
+ SYS_API_HEN_LOW;
+#endif
+ SYS_API_VDDP_OFF;
+ return VIBE_S_SUCCESS;
+}
+
+/*
+** Called by the real-time loop to set PWM_MAG duty cycle
+*/
+IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_SetSamples(VibeUInt8 nActuatorIndex, VibeUInt16 nOutputSignalBitDepth, VibeUInt16 nBufferSizeInBytes, VibeInt8* pForceOutputBuffer)
+{
+ VibeInt8 nForce;
+ int cnt = 0;
+ unsigned char I2C_data[2];
+ int ret = VIBE_S_SUCCESS;
+
+ 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 = ((VibeInt16*)pForceOutputBuffer)[0] >> 8;
+ break;
+ default:
+ /* Unexpected bit depth */
+ return VIBE_E_FAIL;
+ }
+
+ if(nForce == 0)
+ {
+#ifdef ISA1200_GEN_MODE
+ I2C_data[1] = g_nPWM_Duty; // PWM High Duty 50%
+ I2C_data[0]= HCTRL5;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Set] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+#else
+ SYS_API_SET_PWM_DUTY(500); //50%
+#endif
+ if (g_bAmpEnabled == true)
+ {
+ ImmVibeSPI_ForceOut_AmpDisable(0);
+ }
+ }
+ else
+ {
+ if (g_bAmpEnabled != true)
+ {
+ ImmVibeSPI_ForceOut_AmpEnable(0);
+ }
+#ifdef ISA1200_GEN_MODE
+ I2C_data[1] = g_nPWM_Duty + ((g_nPWM_Duty-1)*nForce)/127;
+ I2C_data[0]= HCTRL5;
+ do
+ {
+ ret = SYS_API__I2C__Write(ISA1200_I2C_ADDRESS, 2 /* data length*/, I2C_data);
+ cnt++;
+ }while(VIBE_S_SUCCESS != ret && cnt < RETRY_CNT);
+ if( VIBE_S_SUCCESS != ret) DEBUG_MSG("[ImmVibeSPI_ForceOut_Set] I2C_Write Error, Slave Address = [%d], ret = [%d]\n", I2C_data[0], ret);
+#else
+ SYS_API_SET_PWM_DUTY(500 - (490 * nForce)/127);
+#endif
+ }
+ return VIBE_S_SUCCESS;
+}
+/*
+** Called to set force output frequency parameters
+*/
+IMMVIBESPIAPI VibeStatus ImmVibeSPI_ForceOut_SetFrequency(VibeUInt8 nActuatorIndex, VibeUInt16 nFrequencyParameterID, VibeUInt32 nFrequencyParameterValue)
+{
+ 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;
+}
diff --git a/drivers/motor/Kconfig b/drivers/motor/Kconfig
new file mode 100644
index 0000000..6e333a7
--- /dev/null
+++ b/drivers/motor/Kconfig
@@ -0,0 +1,32 @@
+#
+#
+#
+menuconfig VIBETONZ
+ tristate "Vibetonz"
+ default y
+ help
+ Say Y to enable Vibetonz support.
+
+config MOTOR_DRV_MAX77693
+ tristate "Maxim MAX77693 motor"
+ default y
+ depends on MFD_MAX77693
+ help
+ If you say yes here you will get support for the
+ motor of Maxim MAX77693 PMIC.
+
+config MOTOR_DRV_MAX8997
+ tristate "Maxim MAX8997 motor"
+ default y
+ depends on MFD_MAX8997
+ help
+ If you say yes here you will get support for the
+ motor of Maxim MAX8997 PMIC.
+
+config MOTOR_DRV_ISA1200
+ tristate "ISA1200 motor"
+ default n
+ depends on I2C
+ help
+ Say Y to enalbe the ISA1200 IC.
+
diff --git a/drivers/motor/Makefile b/drivers/motor/Makefile
new file mode 100644
index 0000000..8332949
--- /dev/null
+++ b/drivers/motor/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the vibrator driver ICs with vibrator driver
+#
+
+obj-$(CONFIG_MOTOR_DRV_MAX8997) += max8997_vibrator.o
+obj-$(CONFIG_MOTOR_DRV_MAX77693) += max77693_haptic.o
+obj-$(CONFIG_MOTOR_DRV_ISA1200) += isa1200_vibrator.o
+
+obj-$(CONFIG_VIBETONZ) += vibrator.o
+vibrator-objs += tspdrv.o
diff --git a/drivers/motor/VibeOSKernelLinuxHRTime.c b/drivers/motor/VibeOSKernelLinuxHRTime.c
new file mode 100644
index 0000000..a7e1329
--- /dev/null
+++ b/drivers/motor/VibeOSKernelLinuxHRTime.c
@@ -0,0 +1,233 @@
+/*
+** =========================================================================
+** 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>
+
+#define WATCHDOG_TIMEOUT 10 /* 10 timer cycles = 50ms */
+
+/* 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();
+
+ /* 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);
+}
diff --git a/drivers/motor/isa1200_vibrator.c b/drivers/motor/isa1200_vibrator.c
new file mode 100644
index 0000000..97ccfe1
--- /dev/null
+++ b/drivers/motor/isa1200_vibrator.c
@@ -0,0 +1,386 @@
+/* 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/hrtimer.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/pwm.h>
+#include <linux/wakelock.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/pwm.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/timed_output.h>
+#include <linux/isa1200_vibrator.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+
+#if 0
+#define MOTOR_DEBUG
+#endif
+
+struct isa1200_vibrator_drvdata {
+ struct timed_output_dev dev;
+ struct hrtimer timer;
+ struct work_struct work;
+ struct clk *vib_clk;
+ struct pwm_device *pwm;
+ struct i2c_client *client;
+ int (*gpio_en) (bool) ;
+ spinlock_t lock;
+ bool running;
+ int timeout;
+ int max_timeout;
+ u8 ctrl0;
+ u8 ctrl1;
+ u8 ctrl2;
+ u8 ctrl4;
+ u8 pll;
+ u8 duty;
+ u8 period;
+ int pwm_id;
+ u16 pwm_duty;
+ u16 pwm_period;
+};
+
+
+static struct isa1200_vibrator_drvdata *g_drvdata;
+
+static int isa1200_vibrator_i2c_write(struct i2c_client *client,
+ u8 addr, u8 val)
+{
+ int error = 0;
+ error = i2c_smbus_write_byte_data(client, addr, val);
+ if (error)
+ printk(KERN_ERR "[VIB] Failed to write addr=[0x%x], val=[0x%x]\n",
+ addr, val);
+
+ return error;
+}
+
+int vibtonz_i2c_write(u8 addr, int length, u8 *data)
+{
+ if (NULL == g_drvdata) {
+ printk(KERN_ERR "[VIB] driver is not ready\n");
+ return -EFAULT;
+ }
+
+ if (2 != length)
+ printk(KERN_ERR "[VIB] length should be 2(len:%d)\n",
+ length);
+
+#ifdef MOTOR_DEBUG
+ printk(KERN_DEBUG "[VIB] data : %x, %x\n",
+ data[0], data[1]);
+#endif
+
+ return isa1200_vibrator_i2c_write(g_drvdata->client,
+ data[0], data[1]);
+}
+
+void vibtonz_clk_enable(bool en)
+{
+ if (NULL == g_drvdata) {
+ printk(KERN_ERR "[VIB] driver is not ready\n");
+ return ;
+ }
+
+ if (en) {
+ if (g_drvdata->vib_clk)
+ clk_enable(g_drvdata->vib_clk);
+ else
+ pwm_enable(g_drvdata->pwm);
+ } else {
+ if (g_drvdata->vib_clk)
+ clk_disable(g_drvdata->vib_clk);
+ else
+ pwm_disable(g_drvdata->pwm);
+ }
+}
+
+void vibtonz_clk_config(int duty)
+{
+ u32 duty_ns = 0;
+
+ duty_ns = (u32)(g_drvdata->pwm_period * duty)
+ / 1000;
+
+ pwm_config(g_drvdata->pwm,
+ duty_ns,
+ g_drvdata->pwm_period);
+
+}
+
+void vibtonz_chip_enable(bool en)
+{
+ if (NULL == g_drvdata) {
+ printk(KERN_ERR "[VIB] driver is not ready\n");
+ return ;
+ }
+ g_drvdata->gpio_en(en ? true : false);
+}
+
+static void isa1200_vibrator_hw_init(struct isa1200_vibrator_drvdata *data)
+{
+ data->gpio_en(true);
+ msleep(20);
+ isa1200_vibrator_i2c_write(data->client,
+ HAPTIC_CONTROL_REG0, data->ctrl0);
+ isa1200_vibrator_i2c_write(data->client,
+ HAPTIC_CONTROL_REG1, data->ctrl1);
+ isa1200_vibrator_i2c_write(data->client,
+ HAPTIC_CONTROL_REG2, data->ctrl2);
+ isa1200_vibrator_i2c_write(data->client,
+ HAPTIC_PLL_REG, data->pll);
+ isa1200_vibrator_i2c_write(data->client,
+ HAPTIC_CONTROL_REG4, data->ctrl4);
+ isa1200_vibrator_i2c_write(data->client,
+ HAPTIC_PWM_DUTY_REG, data->period/2);
+ isa1200_vibrator_i2c_write(data->client,
+ HAPTIC_PWM_PERIOD_REG, data->period);
+
+#ifdef MOTOR_DEBUG
+ printk(KERN_DEBUG "[VIB] ctrl0 = 0x%x\n", data->ctrl0);
+ printk(KERN_DEBUG "[VIB] ctrl1 = 0x%x\n", data->ctrl1);
+ printk(KERN_DEBUG "[VIB] ctrl2 = 0x%x\n", data->ctrl2);
+ printk(KERN_DEBUG "[VIB] pll = 0x%x\n", data->pll);
+ printk(KERN_DEBUG "[VIB] ctrl4 = 0x%x\n", data->ctrl4);
+ printk(KERN_DEBUG "[VIB] duty = 0x%x\n", data->period/2);
+ printk(KERN_DEBUG "[VIB] period = 0x%x\n", data->period);
+#endif
+
+}
+
+static void isa1200_vibrator_on(struct isa1200_vibrator_drvdata *data)
+{
+ isa1200_vibrator_i2c_write(data->client,
+ HAPTIC_CONTROL_REG0, data->ctrl0 | CTL0_NORMAL_OP);
+ isa1200_vibrator_i2c_write(data->client,
+ HAPTIC_PWM_DUTY_REG, data->duty - 3);
+#ifdef MOTOR_DEBUG
+ printk(KERN_DEBUG "[VIB] ctrl0 = 0x%x\n", data->ctrl0 | CTL0_NORMAL_OP);
+ printk(KERN_DEBUG "[VIB] duty = 0x%x\n", data->duty);
+#endif
+}
+
+static void isa1200_vibrator_off(struct isa1200_vibrator_drvdata *data)
+{
+ isa1200_vibrator_i2c_write(data->client,
+ HAPTIC_PWM_DUTY_REG, data->period / 2);
+ isa1200_vibrator_i2c_write(data->client,
+ HAPTIC_CONTROL_REG0, data->ctrl0);
+}
+
+static enum hrtimer_restart isa1200_vibrator_timer_func(struct hrtimer *_timer)
+{
+ struct isa1200_vibrator_drvdata *data =
+ container_of(_timer, struct isa1200_vibrator_drvdata, timer);
+
+ data->timeout = 0;
+
+ schedule_work(&data->work);
+ return HRTIMER_NORESTART;
+}
+
+static void isa1200_vibrator_work(struct work_struct *_work)
+{
+ struct isa1200_vibrator_drvdata *data =
+ container_of(_work, struct isa1200_vibrator_drvdata, work);
+
+ if (0 == data->timeout) {
+ if (!data->running)
+ return ;
+
+ data->running = false;
+ isa1200_vibrator_off(data);
+ vibtonz_clk_config(500);
+ vibtonz_clk_enable(false);
+ } else {
+ if (data->running)
+ return ;
+
+ data->running = true;
+ vibtonz_clk_config(999);
+ vibtonz_clk_enable(true);
+ mdelay(1);
+ isa1200_vibrator_on(data);
+ }
+}
+
+static int isa1200_vibrator_get_time(struct timed_output_dev *_dev)
+{
+ struct isa1200_vibrator_drvdata *data =
+ container_of(_dev, struct isa1200_vibrator_drvdata, dev);
+
+ if (hrtimer_active(&data->timer)) {
+ ktime_t r = hrtimer_get_remaining(&data->timer);
+ struct timeval t = ktime_to_timeval(r);
+ return t.tv_sec * 1000 + t.tv_usec / 1000;
+ } else
+ return 0;
+}
+
+static void isa1200_vibrator_enable(struct timed_output_dev *_dev, int value)
+{
+ struct isa1200_vibrator_drvdata *data =
+ container_of(_dev, struct isa1200_vibrator_drvdata, dev);
+ unsigned long flags;
+
+#ifdef MOTOR_DEBUG
+ printk(KERN_DEBUG "[VIB] time = %dms\n", value);
+#endif
+ cancel_work_sync(&data->work);
+ hrtimer_cancel(&data->timer);
+ data->timeout = value;
+ schedule_work(&data->work);
+ spin_lock_irqsave(&data->lock, flags);
+ if (value > 0) {
+ if (value > data->max_timeout)
+ value = data->max_timeout;
+
+ hrtimer_start(&data->timer,
+ ns_to_ktime((u64)value * NSEC_PER_MSEC),
+ HRTIMER_MODE_REL);
+ }
+ spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static int __devinit isa1200_vibrator_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct isa1200_vibrator_platform_data *pdata =
+ client->dev.platform_data;
+ struct isa1200_vibrator_drvdata *ddata;
+ int ret = 0;
+
+ ddata = kzalloc(sizeof(struct isa1200_vibrator_drvdata), GFP_KERNEL);
+ if (NULL == ddata) {
+ printk(KERN_ERR "[VIB] Failed to alloc memory\n");
+ ret = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ddata->client = client;
+ ddata->ctrl0 = pdata->ctrl0;
+ ddata->ctrl1 = pdata->ctrl1;
+ ddata->ctrl2 = pdata->ctrl2;
+ ddata->ctrl4 = pdata->ctrl4;
+ ddata->gpio_en = pdata->gpio_en;
+ if (pdata->get_clk) {
+ ddata->vib_clk = pdata->get_clk();
+ if (IS_ERR(ddata->vib_clk)) {
+ pr_err("[VIB] Failed to request vib_clk.\n");
+ ret = -EFAULT;
+ goto err_request_clk;
+ }
+ } else {
+ ddata->vib_clk = NULL;
+ ddata->pwm = pwm_request(pdata->pwm_id, "vibrator");
+ if (IS_ERR(ddata->pwm)) {
+ pr_err("[VIB] Failed to request pwm.\n");
+ ret = -EFAULT;
+ goto err_request_clk;
+ }
+ ddata->pwm_id = pdata->pwm_id;
+ ddata->pwm_duty = pdata->pwm_duty;
+ ddata->pwm_period = pdata->pwm_period;
+ }
+ ddata->max_timeout = pdata->max_timeout;
+ ddata->pll = pdata->pll;
+ ddata->duty = pdata->duty;
+ ddata->period = pdata->period;
+
+ ddata->dev.name = "vibrator";
+ ddata->dev.get_time = isa1200_vibrator_get_time;
+ ddata->dev.enable = isa1200_vibrator_enable;
+
+ hrtimer_init(&ddata->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ddata->timer.function = isa1200_vibrator_timer_func;
+ INIT_WORK(&ddata->work, isa1200_vibrator_work);
+ spin_lock_init(&ddata->lock);
+
+ i2c_set_clientdata(client, ddata);
+ isa1200_vibrator_hw_init(ddata);
+
+ 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;
+ }
+
+ g_drvdata = ddata;
+
+ return 0;
+
+err_to_dev_reg:
+err_request_clk:
+ kfree(ddata);
+err_free_mem:
+ return ret;
+
+}
+
+static int isa1200_vibrator_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct isa1200_vibrator_drvdata *ddata = i2c_get_clientdata(client);
+ ddata->gpio_en(false);
+ return 0;
+}
+
+static int isa1200_vibrator_resume(struct i2c_client *client)
+{
+ struct isa1200_vibrator_drvdata *ddata = i2c_get_clientdata(client);
+ isa1200_vibrator_hw_init(ddata);
+ 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 i2c_driver isa1200_vibrator_i2c_driver = {
+ .driver = {
+ .name = "isa1200_vibrator",
+ .owner = THIS_MODULE,
+ },
+ .probe = isa1200_vibrator_i2c_probe,
+ .id_table = isa1200_vibrator_device_id,
+ .suspend = isa1200_vibrator_suspend,
+ .resume = isa1200_vibrator_resume,
+};
+
+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);
diff --git a/drivers/motor/max77693_haptic.c b/drivers/motor/max77693_haptic.c
new file mode 100644
index 0000000..f1c816e
--- /dev/null
+++ b/drivers/motor/max77693_haptic.c
@@ -0,0 +1,388 @@
+/*
+ * haptic motor driver for max77693 - max77673_haptic.c
+ *
+ * Copyright (C) 2011 ByungChang Cha <bc.cha@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/timed_output.h>
+#include <linux/hrtimer.h>
+#include <linux/pwm.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-private.h>
+
+struct max77693_haptic_data {
+ struct max77693_dev *max77693;
+ struct i2c_client *i2c;
+ struct i2c_client *pmic_i2c;
+ struct max77693_haptic_platform_data *pdata;
+
+ struct pwm_device *pwm;
+ struct regulator *regulator;
+
+ struct timed_output_dev tout_dev;
+ struct hrtimer timer;
+ unsigned int timeout;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct work;
+ spinlock_t lock;
+ bool running;
+};
+
+struct max77693_haptic_data *g_hap_data;
+
+static void max77693_haptic_i2c(struct max77693_haptic_data *hap_data, bool en)
+{
+ int ret;
+ u8 value = hap_data->pdata->reg2;
+ u8 lscnfg_val = 0x00;
+
+ pr_debug("[VIB] %s %d\n", __func__, en);
+
+ if (en) {
+ value |= MOTOR_EN;
+ lscnfg_val = 0x80;
+ }
+
+ ret = max77693_update_reg(hap_data->pmic_i2c, MAX77693_PMIC_REG_LSCNFG,
+ lscnfg_val, 0x80);
+ if (ret)
+ pr_err("[VIB] i2c update error %d\n", ret);
+
+ ret = max77693_write_reg(hap_data->i2c,
+ MAX77693_HAPTIC_REG_CONFIG2, value);
+ if (ret)
+ pr_err("[VIB] i2c write error %d\n", ret);
+}
+
+static int haptic_get_time(struct timed_output_dev *tout_dev)
+{
+ struct max77693_haptic_data *hap_data
+ = container_of(tout_dev, struct max77693_haptic_data, tout_dev);
+
+ struct hrtimer *timer = &hap_data->timer;
+ if (hrtimer_active(timer)) {
+ ktime_t remain = hrtimer_get_remaining(timer);
+ struct timeval t = ktime_to_timeval(remain);
+ return t.tv_sec * 1000 + t.tv_usec / 1000;
+ }
+ return 0;
+}
+
+static void haptic_enable(struct timed_output_dev *tout_dev, int value)
+{
+ struct max77693_haptic_data *hap_data
+ = container_of(tout_dev, struct max77693_haptic_data, tout_dev);
+
+ struct hrtimer *timer = &hap_data->timer;
+ unsigned long flags;
+
+
+ cancel_work_sync(&hap_data->work);
+ hrtimer_cancel(timer);
+ hap_data->timeout = value;
+ queue_work(hap_data->workqueue, &hap_data->work);
+ spin_lock_irqsave(&hap_data->lock, flags);
+ if (value > 0) {
+ pr_debug("%s value %d\n", __func__, value);
+ value = min(value, (int)hap_data->pdata->max_timeout);
+ hrtimer_start(timer, ns_to_ktime((u64)value * NSEC_PER_MSEC),
+ HRTIMER_MODE_REL);
+ }
+ spin_unlock_irqrestore(&hap_data->lock, flags);
+}
+
+static enum hrtimer_restart haptic_timer_func(struct hrtimer *timer)
+{
+ struct max77693_haptic_data *hap_data
+ = container_of(timer, struct max77693_haptic_data, timer);
+ unsigned long flags;
+
+ hap_data->timeout = 0;
+ queue_work(hap_data->workqueue, &hap_data->work);
+ return HRTIMER_NORESTART;
+}
+
+static int vibetonz_clk_on(struct device *dev, bool en)
+{
+ struct clk *vibetonz_clk = NULL;
+ vibetonz_clk = clk_get(dev, "timers");
+ pr_debug("[VIB] DEV NAME %s %lu\n",
+ dev_name(dev), clk_get_rate(vibetonz_clk));
+
+ if (IS_ERR(vibetonz_clk)) {
+ pr_err("[VIB] failed to get clock for the motor\n");
+ goto err_clk_get;
+ }
+
+ if (en)
+ clk_enable(vibetonz_clk);
+ else
+ clk_disable(vibetonz_clk);
+
+ clk_put(vibetonz_clk);
+ return 0;
+
+err_clk_get:
+ clk_put(vibetonz_clk);
+ return -EINVAL;
+}
+
+static void haptic_work(struct work_struct *work)
+{
+ struct max77693_haptic_data *hap_data
+ = container_of(work, struct max77693_haptic_data, work);
+
+ pr_debug("[VIB] %s\n", __func__);
+ if (hap_data->timeout > 0) {
+ if (hap_data->running)
+ return;
+
+ max77693_haptic_i2c(hap_data, true);
+
+ pwm_config(hap_data->pwm, hap_data->pdata->duty,
+ hap_data->pdata->period);
+ pwm_enable(hap_data->pwm);
+
+ if (hap_data->pdata->motor_en)
+ hap_data->pdata->motor_en(true);
+ else
+ regulator_enable(hap_data->regulator);
+
+ hap_data->running = true;
+ } else {
+ if (!hap_data->running)
+ return;
+
+ if (hap_data->pdata->motor_en)
+ hap_data->pdata->motor_en(false);
+ else
+ regulator_force_disable(hap_data->regulator);
+
+ pwm_disable(hap_data->pwm);
+
+ max77693_haptic_i2c(hap_data, false);
+
+ hap_data->running = false;
+ }
+ return;
+}
+
+#ifdef CONFIG_VIBETONZ
+void vibtonz_en(bool en)
+{
+ if (g_hap_data == NULL) {
+ printk(KERN_ERR "[VIB] the motor is not ready!!!");
+ return ;
+ }
+
+ if (en) {
+ if (g_hap_data->running)
+ return;
+
+ max77693_haptic_i2c(g_hap_data, true);
+ pwm_enable(g_hap_data->pwm);
+
+ if (g_hap_data->pdata->motor_en)
+ g_hap_data->pdata->motor_en(true);
+ else
+ regulator_enable(g_hap_data->regulator);
+
+ g_hap_data->running = true;
+ } else {
+ if (!g_hap_data->running)
+ return;
+
+ if (g_hap_data->pdata->motor_en)
+ g_hap_data->pdata->motor_en(false);
+ else
+ regulator_force_disable(g_hap_data->regulator);
+
+ pwm_disable(g_hap_data->pwm);
+
+ max77693_haptic_i2c(g_hap_data, false);
+
+ g_hap_data->running = false;
+ }
+}
+EXPORT_SYMBOL(vibtonz_en);
+
+void vibtonz_pwm(int nForce)
+{
+ /* add to avoid the glitch issue */
+ static int prev_duty;
+ int pwm_period = 0, pwm_duty = 0;
+
+ if (g_hap_data == NULL) {
+ printk(KERN_ERR "[VIB] the motor is not ready!!!");
+ return ;
+ }
+
+ pwm_period = g_hap_data->pdata->period;
+ pwm_duty = pwm_period / 2 + ((pwm_period / 2 - 2) * nForce) / 127;
+
+ if (pwm_duty > g_hap_data->pdata->duty)
+ pwm_duty = g_hap_data->pdata->duty;
+ else if (pwm_period - pwm_duty > g_hap_data->pdata->duty)
+ pwm_duty = pwm_period - g_hap_data->pdata->duty;
+
+ /* add to avoid the glitch issue */
+ if (prev_duty != pwm_duty) {
+ prev_duty = pwm_duty;
+ pwm_config(g_hap_data->pwm, pwm_duty, pwm_period);
+ }
+}
+EXPORT_SYMBOL(vibtonz_pwm);
+#endif
+
+static int max77693_haptic_probe(struct platform_device *pdev)
+{
+ int error = 0;
+ struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent);
+ struct max77693_platform_data *max77693_pdata
+ = dev_get_platdata(max77693->dev);
+ struct max77693_haptic_platform_data *pdata
+ = max77693_pdata->haptic_data;
+ struct max77693_haptic_data *hap_data;
+
+ pr_debug("[VIB] ++ %s\n", __func__);
+ if (pdata == NULL) {
+ pr_err("%s: no pdata\n", __func__);
+ return -ENODEV;
+ }
+
+ hap_data = kzalloc(sizeof(struct max77693_haptic_data), GFP_KERNEL);
+ if (!hap_data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, hap_data);
+ g_hap_data = hap_data;
+ hap_data->max77693 = max77693;
+ hap_data->i2c = max77693->haptic;
+ hap_data->pmic_i2c = max77693->i2c;
+ hap_data->pdata = pdata;
+
+ hap_data->workqueue = create_singlethread_workqueue("hap_work");
+ INIT_WORK(&(hap_data->work), haptic_work);
+ spin_lock_init(&(hap_data->lock));
+
+ hap_data->pwm = pwm_request(hap_data->pdata->pwm_id, "vibrator");
+ if (IS_ERR(hap_data->pwm)) {
+ pr_err("[VIB] Failed to request pwm\n");
+ error = -EFAULT;
+ goto err_pwm_request;
+ }
+ pwm_config(hap_data->pwm, pdata->period / 2, pdata->period);
+
+ vibetonz_clk_on(&pdev->dev, true);
+
+ if (pdata->init_hw)
+ pdata->init_hw();
+ else
+ hap_data->regulator
+ = regulator_get(NULL, pdata->regulator_name);
+
+ if (IS_ERR(hap_data->regulator)) {
+ pr_err("[VIB] Failed to get vmoter regulator.\n");
+ error = -EFAULT;
+ goto err_regulator_get;
+ }
+
+ /* hrtimer init */
+ hrtimer_init(&hap_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hap_data->timer.function = haptic_timer_func;
+
+ /* timed_output_dev init*/
+ hap_data->tout_dev.name = "vibrator";
+ hap_data->tout_dev.get_time = haptic_get_time;
+ hap_data->tout_dev.enable = haptic_enable;
+
+#ifdef CONFIG_ANDROID_TIMED_OUTPUT
+ error = timed_output_dev_register(&hap_data->tout_dev);
+ if (error < 0) {
+ pr_err("[VIB] Failed to register timed_output : %d\n", error);
+ error = -EFAULT;
+ goto err_timed_output_register;
+ }
+#endif
+
+ pr_debug("[VIB] -- %s\n", __func__);
+
+ return error;
+
+err_timed_output_register:
+ regulator_put(hap_data->regulator);
+err_regulator_get:
+ pwm_free(hap_data->pwm);
+err_pwm_request:
+ kfree(hap_data);
+ g_hap_data = NULL;
+ return error;
+}
+
+static int __devexit max77693_haptic_remove(struct platform_device *pdev)
+{
+ struct max77693_haptic_data *data = platform_get_drvdata(pdev);
+#ifdef CONFIG_ANDROID_TIMED_OUTPUT
+ timed_output_dev_unregister(&data->tout_dev);
+#endif
+ regulator_put(data->regulator);
+ pwm_free(data->pwm);
+ destroy_workqueue(data->workqueue);
+ kfree(data);
+ g_hap_data = NULL;
+
+ return 0;
+}
+
+static int max77693_haptic_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ vibetonz_clk_on(&pdev->dev, false);
+ return 0;
+}
+static int max77693_haptic_resume(struct platform_device *pdev)
+{
+ vibetonz_clk_on(&pdev->dev, true);
+ return 0;
+}
+
+static struct platform_driver max77693_haptic_driver = {
+ .probe = max77693_haptic_probe,
+ .remove = max77693_haptic_remove,
+ .suspend = max77693_haptic_suspend,
+ .resume = max77693_haptic_resume,
+ .driver = {
+ .name = "max77693-haptic",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init max77693_haptic_init(void)
+{
+ pr_debug("[VIB] %s\n", __func__);
+ return platform_driver_register(&max77693_haptic_driver);
+}
+module_init(max77693_haptic_init);
+
+static void __exit max77693_haptic_exit(void)
+{
+ platform_driver_unregister(&max77693_haptic_driver);
+}
+module_exit(max77693_haptic_exit);
+
+MODULE_AUTHOR("ByungChang Cha <bc.cha@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MAX77693 haptic driver");
diff --git a/drivers/motor/max8997_vibrator.c b/drivers/motor/max8997_vibrator.c
new file mode 100644
index 0000000..c34716b
--- /dev/null
+++ b/drivers/motor/max8997_vibrator.c
@@ -0,0 +1,336 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/timed_output.h>
+#include <linux/hrtimer.h>
+#include <linux/pwm.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/mfd/max8997.h>
+#include <linux/mfd/max8997-private.h>
+
+#ifdef CONFIG_MACH_Q1_BD
+#include "mach/gpio.h"
+#endif
+
+struct vibrator_drvdata {
+ struct max8997_motor_data *pdata;
+ struct pwm_device *pwm;
+ struct regulator *regulator;
+ struct i2c_client *client;
+ struct timed_output_dev dev;
+ struct hrtimer timer;
+ struct work_struct work;
+ spinlock_t lock;
+ bool running;
+ int timeout;
+};
+
+#ifdef CONFIG_VIBETONZ
+struct vibrator_drvdata *g_data;
+#endif
+
+static int vibetonz_clk_on(struct device *dev, bool en)
+{
+ struct clk *vibetonz_clk = NULL;
+ vibetonz_clk = clk_get(dev, "timers");
+ if (IS_ERR(vibetonz_clk)) {
+ pr_err("[VIB] failed to get clock for the motor\n");
+ goto err_clk_get;
+ }
+ if (en)
+ clk_enable(vibetonz_clk);
+ else
+ clk_disable(vibetonz_clk);
+ clk_put(vibetonz_clk);
+ return 0;
+
+err_clk_get:
+ clk_put(vibetonz_clk);
+ return -EINVAL;
+}
+
+static void i2c_max8997_hapticmotor(struct vibrator_drvdata *data, bool en)
+{
+ int ret;
+ u8 value = data->pdata->reg2;
+
+ /* the setting of reg2 should be set */
+ if (0 == value)
+#if defined(CONFIG_MACH_P2)
+ value = MOTOR_LRA | EXT_PWM | DIVIDER_128;
+#else
+ value = MOTOR_LRA | EXT_PWM | DIVIDER_256;
+#endif
+
+ if (en)
+ value |= MOTOR_EN;
+
+ ret = max8997_write_reg(data->client,
+ MAX8997_MOTOR_REG_CONFIG2, value);
+ if (ret < 0)
+ pr_err("[VIB] i2c write error\n");
+}
+
+static enum hrtimer_restart vibrator_timer_func(struct hrtimer *_timer)
+{
+ struct vibrator_drvdata *data =
+ container_of(_timer, struct vibrator_drvdata, timer);
+
+ data->timeout = 0;
+
+ schedule_work(&data->work);
+ return HRTIMER_NORESTART;
+}
+
+static void vibrator_work(struct work_struct *_work)
+{
+ struct vibrator_drvdata *data =
+ container_of(_work, struct vibrator_drvdata, work);
+
+ printk(KERN_DEBUG "[VIB] time = %dms\n", data->timeout);
+
+ if (0 == data->timeout) {
+ if (!data->running)
+ return ;
+ pwm_disable(data->pwm);
+ i2c_max8997_hapticmotor(data, false);
+ if (data->pdata->motor_en)
+ data->pdata->motor_en(false);
+ else
+ regulator_force_disable(data->regulator);
+ data->running = false;
+
+ } else {
+ if (data->running)
+ return ;
+ if (data->pdata->motor_en)
+ data->pdata->motor_en(true);
+ else
+ regulator_enable(data->regulator);
+ i2c_max8997_hapticmotor(data, true);
+ pwm_config(data->pwm,
+ data->pdata->duty, data->pdata->period);
+ pwm_enable(data->pwm);
+
+ data->running = true;
+ }
+}
+
+static int vibrator_get_time(struct timed_output_dev *_dev)
+{
+ struct vibrator_drvdata *data =
+ container_of(_dev, struct vibrator_drvdata, dev);
+
+ if (hrtimer_active(&data->timer)) {
+ ktime_t r = hrtimer_get_remaining(&data->timer);
+ struct timeval t = ktime_to_timeval(r);
+ return t.tv_sec * 1000 + t.tv_usec / 1000;
+ } else
+ return 0;
+}
+
+static void vibrator_enable(struct timed_output_dev *_dev, int value)
+{
+ struct vibrator_drvdata *data =
+ container_of(_dev, struct vibrator_drvdata, dev);
+ unsigned long flags;
+
+ cancel_work_sync(&data->work);
+ hrtimer_cancel(&data->timer);
+ data->timeout = value;
+ schedule_work(&data->work);
+ spin_lock_irqsave(&data->lock, flags);
+ if (value > 0) {
+ if (value > data->pdata->max_timeout)
+ value = data->pdata->max_timeout;
+
+ hrtimer_start(&data->timer,
+ ns_to_ktime((u64)value * NSEC_PER_MSEC),
+ HRTIMER_MODE_REL);
+ }
+ spin_unlock_irqrestore(&data->lock, flags);
+}
+
+#ifdef CONFIG_VIBETONZ
+void vibtonz_en(bool en)
+{
+ struct vibrator_drvdata *data = g_data;
+
+ if (en) {
+ if (data->running)
+ return ;
+ if (data->pdata->motor_en)
+ data->pdata->motor_en(true);
+ else
+ regulator_enable(data->regulator);
+ i2c_max8997_hapticmotor(data, true);
+ pwm_enable(data->pwm);
+ data->running = true;
+ } else {
+ if (!data->running)
+ return ;
+
+ pwm_disable(data->pwm);
+ i2c_max8997_hapticmotor(data, false);
+ if (data->pdata->motor_en)
+ data->pdata->motor_en(false);
+ else
+ regulator_force_disable(data->regulator);
+ data->running = false;
+ }
+}
+EXPORT_SYMBOL(vibtonz_en);
+
+void vibtonz_pwm(int nForce)
+{
+ struct vibrator_drvdata *data = g_data;
+ /* add to avoid the glitch issue */
+ static int prev_duty;
+ int pwm_period = data->pdata->period;
+ int pwm_duty = pwm_period/2 + ((pwm_period/2 - 2) * nForce)/127;
+
+#if defined(CONFIG_MACH_P4)
+ if (pwm_duty > data->pdata->duty)
+ pwm_duty = data->pdata->duty;
+ else if (pwm_period - pwm_duty > data->pdata->duty)
+ pwm_duty = pwm_period - data->pdata->duty;
+#endif
+
+ /* add to avoid the glitch issue */
+ if (prev_duty != pwm_duty) {
+ prev_duty = pwm_duty;
+ pwm_config(data->pwm, pwm_duty, pwm_period);
+ }
+}
+EXPORT_SYMBOL(vibtonz_pwm);
+#endif
+
+static int __devinit vibrator_probe(struct platform_device *pdev)
+{
+ struct max8997_dev *max8997 = dev_get_drvdata(pdev->dev.parent);
+ struct max8997_platform_data *max8997_pdata
+ = dev_get_platdata(max8997->dev);
+ struct max8997_motor_data *pdata = max8997_pdata->motor;
+ struct vibrator_drvdata *ddata;
+ int error = 0;
+
+ ddata = kzalloc(sizeof(struct vibrator_drvdata), GFP_KERNEL);
+ if (NULL == ddata) {
+ pr_err("[VIB] Failed to alloc memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ if (pdata->init_hw)
+ pdata->init_hw();
+ else {
+ ddata->regulator = regulator_get(NULL, "vmotor");
+ if (IS_ERR(ddata->regulator)) {
+ pr_err("[VIB] Failed to get vmoter regulator.\n");
+ error = -EFAULT;
+ goto err_regulator_get;
+ }
+ }
+
+ ddata->pdata = pdata;
+ ddata->dev.name = "vibrator";
+ ddata->dev.get_time = vibrator_get_time;
+ ddata->dev.enable = vibrator_enable;
+ ddata->client = max8997->hmotor;
+
+ platform_set_drvdata(pdev, ddata);
+
+ hrtimer_init(&ddata->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ddata->timer.function = vibrator_timer_func;
+ INIT_WORK(&ddata->work, vibrator_work);
+ spin_lock_init(&ddata->lock);
+
+ ddata->pwm = pwm_request(pdata->pwm_id, "vibrator");
+ if (IS_ERR(ddata->pwm)) {
+ pr_err("[VIB] Failed to request pwm.\n");
+ error = -EFAULT;
+ goto err_pwm_request;
+ }
+ pwm_config(ddata->pwm,
+ ddata->pdata->period/2, ddata->pdata->period);
+
+ vibetonz_clk_on(&pdev->dev, true);
+
+ error = timed_output_dev_register(&ddata->dev);
+ if (error < 0) {
+ pr_err("[VIB] Failed to register timed_output : %d\n", error);
+ error = -EFAULT;
+ goto err_timed_output_register;
+ }
+
+#ifdef CONFIG_VIBETONZ
+ g_data = ddata;
+#endif
+
+ return 0;
+
+err_timed_output_register:
+ timed_output_dev_unregister(&ddata->dev);
+err_regulator_get:
+ regulator_put(ddata->regulator);
+err_pwm_request:
+ pwm_free(ddata->pwm);
+err_free_mem:
+ kfree(ddata);
+ return error;
+}
+
+static int __devexit vibrator_remove(struct platform_device *pdev)
+{
+ struct vibrator_drvdata *data = platform_get_drvdata(pdev);
+ timed_output_dev_unregister(&data->dev);
+#ifdef CONFIG_MACH_Q1_BD
+ gpio_free(GPIO_MOTOR_EN);
+#else
+ regulator_put(data->regulator);
+#endif
+ pwm_free(data->pwm);
+ kfree(data);
+ return 0;
+}
+
+static int vibrator_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ vibetonz_clk_on(&pdev->dev, false);
+ return 0;
+}
+static int vibrator_resume(struct platform_device *pdev)
+{
+ vibetonz_clk_on(&pdev->dev, true);
+ return 0;
+}
+
+static struct platform_driver vibrator_driver = {
+ .probe = vibrator_probe,
+ .remove = __devexit_p(vibrator_remove),
+ .suspend = vibrator_suspend,
+ .resume = vibrator_resume,
+ .driver = {
+ .name = "max8997-hapticmotor",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init vibrator_init(void)
+{
+ return platform_driver_register(&vibrator_driver);
+}
+
+static void __exit vibrator_exit(void)
+{
+ platform_driver_unregister(&vibrator_driver);
+}
+
+late_initcall(vibrator_init);
+module_exit(vibrator_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("vibrator driver");
diff --git a/drivers/motor/tspdrv.c b/drivers/motor/tspdrv.c
new file mode 100644
index 0000000..257fbfc
--- /dev/null
+++ b/drivers/motor/tspdrv.c
@@ -0,0 +1,587 @@
+/*
+** =========================================================================
+** File:
+** tspdrv.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 <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/wakelock.h>
+#include <linux/io.h>
+#include <mach/map.h>
+#include "tspdrv.h"
+#ifdef CONFIG_MOTOR_DRV_ISA1200
+#include "ImmVibeSPI_isa1200.c"
+#else
+#include "ImmVibeSPI.c"
+#endif
+#if defined(VIBE_DEBUG) && defined(VIBE_RECORD)
+#include <tspdrvRecorder.c>
+#endif
+
+/* 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];
+
+/* #define VIBE_TUNING */
+/* #define VIBE_ENABLE_SYSTEM_TIMER */
+/* #define IMPLEMENT_AS_CHAR_DRIVER */
+
+/* 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
+
+#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
+
+/* 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
+};
+
+#ifndef IMPLEMENT_AS_CHAR_DRIVER
+static struct miscdevice miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = MODULE_NAME,
+ .fops = &fops
+};
+#endif
+
+static int suspend(struct platform_device *pdev, pm_message_t state);
+static int resume(struct platform_device *pdev);
+static struct platform_driver platdrv = {
+ .suspend = suspend,
+ .resume = resume,
+ .driver = {
+ .name = MODULE_NAME,
+ },
+};
+
+static void platform_release(struct device *dev);
+static struct platform_device platdev = {
+ .name = MODULE_NAME,
+ /* means that there is only one device */
+ .id = -1,
+ .dev = {
+ .platform_data = NULL,
+ /* a warning is thrown during rmmod if this is absent */
+ .release = platform_release,
+ },
+};
+
+/* Module info */
+MODULE_AUTHOR("Immersion Corporation");
+MODULE_DESCRIPTION("TouchSense Kernel Module");
+MODULE_LICENSE("GPL v2");
+
+#ifdef VIBE_ENABLE_SYSTEM_TIMER
+int vibetonz_clk_on(struct device *dev)
+{
+ struct clk *vibetonz_clk = NULL;
+ /*printk(KERN_ERR"[VIBRATOR]vibetonz_clk_on is called\n");*/
+ vibetonz_clk = clk_get(dev, "timers");
+ if (IS_ERR(vibetonz_clk)) {
+ printk(KERN_ERR "tspdrv: failed to get clock for vibetonz\n");
+ goto err_clk0;
+ }
+ clk_enable(vibetonz_clk);
+ clk_put(vibetonz_clk);
+
+ return 0;
+
+err_clk0:
+ clk_put(vibetonz_clk);
+ return -EINVAL;
+}
+
+int vibetonz_clk_off(struct device *dev)
+{
+ struct clk *vibetonz_clk = NULL;
+
+ vibetonz_clk = clk_get(dev, "timers");
+ if (IS_ERR(vibetonz_clk)) {
+ DbgOut((KERN_ERR "tspdrv: failed to get clock for vibetonz\n"));
+ goto err_clk0;
+ }
+ clk_disable(vibetonz_clk);
+ clk_put(vibetonz_clk);
+
+ return 0;
+
+err_clk0:
+ clk_put(vibetonz_clk);
+
+ return -EINVAL;
+}
+#else
+int vibetonz_clk_on(struct device *dev)
+{
+ return -EINVAL;
+}
+
+int vibetonz_clk_off(struct device *dev)
+{
+ return -EINVAL;
+}
+#endif /* VIBE_ENABLE_SYSTEM_TIMER */
+
+int init_module(void)
+{
+ int nRet, i; /* initialized below */
+ nRet = 0;
+
+#ifdef IMPLEMENT_AS_CHAR_DRIVER
+ printk(KERN_ERR
+ "[VIBRATOR]IMPLEMENT_AS_CHAR_DRIVER\n");
+ g_nMajor = register_chrdev(0, MODULE_NAME, &fops);
+ if (g_nMajor < 0) {
+ printk(KERN_ERR"[VIBRATOR]tspdrv: can't get major number.\n");
+ return g_nMajor;
+ }
+#else
+ nRet = misc_register(&miscdev);
+ if (nRet) {
+ printk(KERN_ERR "[VIBRATOR]tspdrv: misc_register failed.\n");
+ return nRet;
+ }
+#endif
+
+ nRet = platform_device_register(&platdev);
+ if (nRet) {
+ printk(KERN_ERR "tspdrv: platform_device_register failed.\n");
+ goto err_platform_dev_reg;
+ }
+
+ nRet = platform_driver_register(&platdrv);
+ if (nRet) {
+ printk(KERN_ERR "tspdrv: platform_driver_register failed.\n");
+ goto err_platform_drv_reg;
+ }
+
+ DbgRecorderInit(());
+
+ vibetonz_clk_on(&platdev.dev);
+
+ ImmVibeSPI_ForceOut_Initialize();
+ VibeOSKernelLinuxInitTimer();
+
+ /* 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);
+
+ 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");
+ return 0;
+
+err_platform_drv_reg:
+ platform_device_unregister(&platdev);
+err_platform_dev_reg:
+#ifdef IMPLEMENT_AS_CHAR_DRIVER
+ unregister_chrdev(g_nMajor, MODULE_NAME);
+#else
+ misc_deregister(&miscdev);
+#endif
+ return nRet;
+}
+
+void cleanup_module(void)
+{
+ DbgOut((KERN_INFO "tspdrv: cleanup_module.\n"));
+
+ DbgRecorderTerminate(());
+
+ VibeOSKernelLinuxTerminateTimer();
+ ImmVibeSPI_ForceOut_Terminate();
+
+ platform_driver_unregister(&platdrv);
+ platform_device_unregister(&platdev);
+
+#ifdef IMPLEMENT_AS_CHAR_DRIVER
+ unregister_chrdev(g_nMajor, MODULE_NAME);
+#else
+ misc_deregister(&miscdev);
+#endif
+ wake_lock_destroy(&vib_wake_lock);
+}
+
+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;
+ }
+
+ /* 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;
+ }
+
+ /* Check buffer size */
+ if ((count <= SPI_HEADER_SIZE) || (count > SPI_BUFFER_SIZE)) {
+ DbgOut((KERN_ERR "tspdrv: invalid write buffer size.\n"));
+ return 0;
+ }
+
+ while (i < count) {
+ int nIndexFreeBuffer; /* initialized below */
+
+ samples_buffer* pInputBuffer = (samples_buffer *)
+ (&g_cWriteBuffer[i]);
+
+ if ((i + SPI_HEADER_SIZE) >= count) {
+ /*
+ ** Index is about to go beyond the buffer size.
+ ** (Should never happen).
+ */
+ DbgOut((KERN_EMERG "tspdrv: invalid buffer index.\n"));
+ }
+
+ /* 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"));
+ }
+
+ /* 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;
+ }
+
+ /* 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 (true == 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:
+ file->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
+ */
+ if (!g_bStopRequested)
+ ImmVibeSPI_ForceOut_AmpDisable(arg);
+ wake_unlock(&vib_wake_lock);
+ break;
+
+ case TSPDRV_GET_NUM_ACTUATORS:
+ return NUM_ACTUATORS;
+ }
+
+ return 0;
+}
+
+static int suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int ret;
+
+ if (g_bIsPlaying) {
+ ret = -EBUSY;
+ } else {
+ /* Disable system timers */
+ vibetonz_clk_off(&pdev->dev);
+
+ ret = 0;
+ }
+
+ DbgOut((KERN_DEBUG "tspdrv: %s (%d).\n", __func__, ret));
+ return ret;
+}
+
+static int resume(struct platform_device *pdev)
+{
+ u32 __iomem *pram;
+
+ /* Restart system timers */
+ vibetonz_clk_on(&pdev->dev);
+
+ /* Restore system timers configuration */
+ pram = ioremap(S5P_PA_TIMER, 4);
+ writel(0x0F00, pram);
+ iounmap(pram);
+
+ DbgOut((KERN_DEBUG "tspdrv: %s.\n", __func__));
+ return 0;
+}
+
+static void platform_release(struct device *dev)
+{
+ DbgOut((KERN_INFO "tspdrv: platform_release.\n"));
+}
+
+late_initcall(init_module);
+module_exit(cleanup_module);
diff --git a/drivers/motor/tspdrv.h b/drivers/motor/tspdrv.h
new file mode 100644
index 0000000..8bbb333
--- /dev/null
+++ b/drivers/motor/tspdrv.h
@@ -0,0 +1,110 @@
+/*
+** =========================================================================
+** 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
+
+/* Constants */
+#define MODULE_NAME "tspdrv"
+#define TSPDRV "/dev/"MODULE_NAME
+#define TSPDRV_MAGIC_NUMBER 0x494D4D52
+#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 */
+
+#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__ */
+
+
+int regulator_hapticmotor_enabled;
+
+
+#endif /* _TSPDRV_H */