diff options
| author | Evan Wilson <evan@oliodevices.com> | 2015-02-13 16:19:39 -0800 | 
|---|---|---|
| committer | Evan Wilson <evan@oliodevices.com> | 2015-02-13 16:19:39 -0800 | 
| commit | 638b8c6f24cd657aa2187601ab2b0b76b30e5eef (patch) | |
| tree | c289fef33c11ada17109daf7f9e63ee5d94c5ec1 | |
| parent | b646f671ca7ecf438c84ff99c9e6aaba29685276 (diff) | |
| download | olio-linux-3.10-vibe-drv2605.tar.xz olio-linux-3.10-vibe-drv2605.zip | |
Initial commit of the drv2605 drivervibe-drv2605
Change-Id: I9cdb775e63d2554a7192c40d95b093eb4387101e
| -rw-r--r-- | drivers/haptics/Kconfig | 17 | ||||
| -rw-r--r-- | drivers/haptics/Makefile | 1 | ||||
| -rw-r--r-- | drivers/haptics/drv2605.c | 988 | ||||
| -rw-r--r-- | include/linux/haptics/drv2605.h | 458 | 
4 files changed, 1464 insertions, 0 deletions
| diff --git a/drivers/haptics/Kconfig b/drivers/haptics/Kconfig new file mode 100644 index 00000000000..f3257f091bf --- /dev/null +++ b/drivers/haptics/Kconfig @@ -0,0 +1,17 @@ +# +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/kconfig-language.txt. +# +# Haptics device driver configuration. +# + +menuconfig HAPTICS_DRV2605 +	tristate "DRV2605 Haptics Controller" +	default n +	---help--- +	  If you have a vibrator controller by the DRV2605, say Y. + +	  To compile this as a module, choose M here: +	  the module will be called drv2605. + +	  If unsure, say N.
\ No newline at end of file diff --git a/drivers/haptics/Makefile b/drivers/haptics/Makefile new file mode 100644 index 00000000000..a289790929c --- /dev/null +++ b/drivers/haptics/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_HAPTICS_DRV2605)	+=	drv2605.o
\ No newline at end of file diff --git a/drivers/haptics/drv2605.c b/drivers/haptics/drv2605.c new file mode 100644 index 00000000000..8077bda216a --- /dev/null +++ b/drivers/haptics/drv2605.c @@ -0,0 +1,988 @@ +/* +** ============================================================================= +** Copyright (c) 2014  Texas Instruments Inc. +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +** +** File: +**     drv2605.c +** +** Description: +**     DRV2605 chip driver +** +** ============================================================================= +*/ +#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/i2c.h> +#include <linux/semaphore.h> +#include <linux/device.h> +#include <linux/syscalls.h> +#include <asm/uaccess.h> +#include <linux/gpio.h> +#include <linux/sched.h> +#include <linux/spinlock_types.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/haptics/drv2605.h> + +static struct drv2605_data *pDRV2605data = NULL; + +static int drv2605_reg_read(struct drv2605_data *pDrv2605data, unsigned int reg) +{ +	unsigned int val; +	int ret; + +	ret = regmap_read(pDrv2605data->regmap, reg, &val); + +	if (ret < 0) +		return ret; +	else +		return val; +} + +static int drv2605_reg_write(struct drv2605_data *pDrv2605data, unsigned char reg, char val) +{ +    return regmap_write(pDrv2605data->regmap, reg, val); +} + +static int drv2605_bulk_read(struct drv2605_data *pDrv2605data, unsigned char reg, unsigned int count, u8 *buf) +{ +	return regmap_bulk_read(pDrv2605data->regmap, reg, buf, count); +} + +static int drv2605_bulk_write(struct drv2605_data *pDrv2605data, unsigned char reg, unsigned int count, const u8 *buf) +{ +	return regmap_bulk_write(pDrv2605data->regmap, reg, buf, count); +} + +static int drv2605_set_bits(struct drv2605_data *pDrv2605data, unsigned char reg, unsigned char mask, unsigned char val) +{ +	return regmap_update_bits(pDrv2605data->regmap, reg, mask, val); +} + +static int drv2605_set_go_bit(struct drv2605_data *pDrv2605data, unsigned char val) +{ +	return drv2605_reg_write(pDrv2605data, GO_REG, (val&0x01)); +} + +static void drv2605_poll_go_bit(struct drv2605_data *pDrv2605data) +{ +    while (drv2605_reg_read(pDrv2605data, GO_REG) == GO) +      schedule_timeout_interruptible(msecs_to_jiffies(GO_BIT_POLL_INTERVAL)); +} + +static int drv2605_select_library(struct drv2605_data *pDrv2605data, unsigned char lib) +{ +	return drv2605_reg_write(pDrv2605data, LIBRARY_SELECTION_REG, (lib&0x07)); +} + +static int drv2605_set_rtp_val(struct drv2605_data *pDrv2605data, char value) +{ +	/* please be noted: in unsigned mode, maximum is 0xff, in signed mode, maximum is 0x7f */ +	return drv2605_reg_write(pDrv2605data, REAL_TIME_PLAYBACK_REG, value); +} + +static int drv2605_set_waveform_sequence(struct drv2605_data *pDrv2605data, unsigned char* seq, unsigned int size) +{ +	return drv2605_bulk_write(pDrv2605data, WAVEFORM_SEQUENCER_REG, (size>WAVEFORM_SEQUENCER_MAX)?WAVEFORM_SEQUENCER_MAX:size, seq); +} + +static void drv2605_change_mode(struct drv2605_data *pDrv2605data, char work_mode, char dev_mode) +{ +	/* please be noted : LRA open loop cannot be used with analog input mode */ +	if(dev_mode == DEV_IDLE){ +		pDrv2605data->dev_mode = dev_mode; +		pDrv2605data->work_mode = work_mode; +	}else if(dev_mode == DEV_STANDBY){ +		if(pDrv2605data->dev_mode != DEV_STANDBY){ +			pDrv2605data->dev_mode = DEV_STANDBY; +			drv2605_reg_write(pDrv2605data, MODE_REG, MODE_STANDBY); +			schedule_timeout_interruptible(msecs_to_jiffies(WAKE_STANDBY_DELAY)); +		} +		pDrv2605data->work_mode = WORK_IDLE; +	}else if(dev_mode == DEV_READY){ +		if((work_mode != pDrv2605data->work_mode) +			||(dev_mode != pDrv2605data->dev_mode)){ +			pDrv2605data->work_mode = work_mode; +			pDrv2605data->dev_mode = dev_mode; +			if((pDrv2605data->work_mode == WORK_VIBRATOR) +				||(pDrv2605data->work_mode == WORK_PATTERN_RTP_ON) +				||(pDrv2605data->work_mode == WORK_SEQ_RTP_ON) +				||(pDrv2605data->work_mode == WORK_RTP)){ +					drv2605_reg_write(pDrv2605data, MODE_REG, MODE_REAL_TIME_PLAYBACK); +			}else if(pDrv2605data->work_mode == WORK_AUDIO2HAPTIC){ +				drv2605_reg_write(pDrv2605data, MODE_REG, MODE_AUDIOHAPTIC); +			}else if(pDrv2605data->work_mode == WORK_CALIBRATION){ +				drv2605_reg_write(pDrv2605data, MODE_REG, AUTO_CALIBRATION); +			}else{ +				drv2605_reg_write(pDrv2605data, MODE_REG, MODE_INTERNAL_TRIGGER); +				schedule_timeout_interruptible(msecs_to_jiffies(STANDBY_WAKE_DELAY)); +			} +		} +	} +} + +static void setAudioHapticsEnabled(struct drv2605_data *pDrv2605data, int enable) +{ +    if (enable) +    { +		if(pDrv2605data->work_mode != WORK_AUDIO2HAPTIC){ +			pDrv2605data->vibrator_is_playing = YES; +			drv2605_change_mode(pDrv2605data, WORK_IDLE, DEV_READY); + +			drv2605_set_bits(pDrv2605data, +					Control1_REG, +					Control1_REG_AC_COUPLE_MASK, +					AC_COUPLE_ENABLED ); + +			drv2605_set_bits(pDrv2605data, +					Control3_REG, +					Control3_REG_PWMANALOG_MASK, +					INPUT_ANALOG); + +			drv2605_change_mode(pDrv2605data, WORK_AUDIO2HAPTIC, DEV_READY); +			switch_set_state(&pDrv2605data->sw_dev, SW_STATE_AUDIO2HAPTIC); +		} +    } else { +        // Chip needs to be brought out of standby to change the registers +		if(pDrv2605data->work_mode == WORK_AUDIO2HAPTIC){ +			pDrv2605data->vibrator_is_playing = NO; +			drv2605_change_mode(pDrv2605data, WORK_IDLE, DEV_READY); + +			drv2605_set_bits(pDrv2605data, +					Control1_REG, +					Control1_REG_AC_COUPLE_MASK, +					AC_COUPLE_DISABLED ); + +			drv2605_set_bits(pDrv2605data, +					Control3_REG, +					Control3_REG_PWMANALOG_MASK, +					INPUT_PWM); + +			switch_set_state(&pDrv2605data->sw_dev, SW_STATE_IDLE); +			drv2605_change_mode(pDrv2605data, WORK_IDLE, DEV_STANDBY); // Disable audio-to-haptics +		} +    } +} + +static void play_effect(struct drv2605_data *pDrv2605data) +{ +	switch_set_state(&pDrv2605data->sw_dev, SW_STATE_SEQUENCE_PLAYBACK); +	drv2605_change_mode(pDrv2605data, WORK_SEQ_PLAYBACK, DEV_READY); +    drv2605_set_waveform_sequence(pDrv2605data, pDrv2605data->sequence, WAVEFORM_SEQUENCER_MAX); +	pDrv2605data->vibrator_is_playing = YES; +    drv2605_set_go_bit(pDrv2605data, GO); + +    while((drv2605_reg_read(pDrv2605data, GO_REG) == GO) && (pDrv2605data->should_stop == NO)){ +        schedule_timeout_interruptible(msecs_to_jiffies(GO_BIT_POLL_INTERVAL)); +	} + +	if(pDrv2605data->should_stop == YES){ +		drv2605_set_go_bit(pDrv2605data, STOP); +	} + +    if (pDrv2605data->audio_haptics_enabled){ +        setAudioHapticsEnabled(pDrv2605data, YES); +    } else { +        drv2605_change_mode(pDrv2605data, WORK_IDLE, DEV_STANDBY); +		switch_set_state(&pDrv2605data->sw_dev, SW_STATE_IDLE); +		pDrv2605data->vibrator_is_playing = NO; +		wake_unlock(&pDrv2605data->wklock); +    } +} + +static void play_Pattern_RTP(struct drv2605_data *pDrv2605data) +{ +	if(pDrv2605data->work_mode == WORK_PATTERN_RTP_ON){ +		drv2605_change_mode(pDrv2605data, WORK_PATTERN_RTP_OFF, DEV_READY); +		if(pDrv2605data->repeat_times == 0){ +			drv2605_change_mode(pDrv2605data, WORK_IDLE, DEV_STANDBY); +			pDrv2605data->vibrator_is_playing = NO; +			switch_set_state(&pDrv2605data->sw_dev, SW_STATE_IDLE); +			wake_unlock(&pDrv2605data->wklock); +		}else{ +			hrtimer_start(&pDrv2605data->timer, ns_to_ktime((u64)pDrv2605data->silience_time * NSEC_PER_MSEC), HRTIMER_MODE_REL); +		} +	}else if(pDrv2605data->work_mode == WORK_PATTERN_RTP_OFF){ +		pDrv2605data->repeat_times--; +		drv2605_change_mode(pDrv2605data, WORK_PATTERN_RTP_ON, DEV_READY); +		hrtimer_start(&pDrv2605data->timer, ns_to_ktime((u64)pDrv2605data->vibration_time * NSEC_PER_MSEC), HRTIMER_MODE_REL); +	} +} + +static void play_Seq_RTP(struct drv2605_data *pDrv2605data) +{ +	if(pDrv2605data->RTPSeq.RTPindex < pDrv2605data->RTPSeq.RTPCounts){ +		int RTPTime = pDrv2605data->RTPSeq.RTPData[pDrv2605data->RTPSeq.RTPindex] >> 8; +		int RTPVal = pDrv2605data->RTPSeq.RTPData[pDrv2605data->RTPSeq.RTPindex] & 0x00ff ; + +		pDrv2605data->vibrator_is_playing = YES; +		pDrv2605data->RTPSeq.RTPindex++; +		drv2605_change_mode(pDrv2605data, WORK_SEQ_RTP_ON, DEV_READY); +		drv2605_set_rtp_val(pDrv2605data,  RTPVal); + +		hrtimer_start(&pDrv2605data->timer, ns_to_ktime((u64)RTPTime * NSEC_PER_MSEC), HRTIMER_MODE_REL); +	}else{ +		drv2605_change_mode(pDrv2605data, WORK_IDLE, DEV_STANDBY); +		pDrv2605data->vibrator_is_playing = NO; +		switch_set_state(&pDrv2605data->sw_dev, SW_STATE_IDLE); +		wake_unlock(&pDrv2605data->wklock); +	} +} + +static void vibrator_off(struct drv2605_data *pDrv2605data) +{ +    if (pDrv2605data->vibrator_is_playing) { +		if(pDrv2605data->audio_haptics_enabled == YES){ +			setAudioHapticsEnabled(pDrv2605data, YES); +		}else{ +			pDrv2605data->vibrator_is_playing = NO; +			drv2605_set_go_bit(pDrv2605data, STOP); +			drv2605_change_mode(pDrv2605data, WORK_IDLE, DEV_STANDBY); +			switch_set_state(&pDrv2605data->sw_dev, SW_STATE_IDLE); +			wake_unlock(&pDrv2605data->wklock); +		} +    } +} + +static void drv2605_stop(struct drv2605_data *pDrv2605data) +{ +	if(pDrv2605data->vibrator_is_playing){ +		if(pDrv2605data->work_mode == WORK_AUDIO2HAPTIC){ +			setAudioHapticsEnabled(pDrv2605data, NO); +		}else if((pDrv2605data->work_mode == WORK_VIBRATOR) +				||(pDrv2605data->work_mode == WORK_PATTERN_RTP_ON) +				||(pDrv2605data->work_mode == WORK_PATTERN_RTP_OFF) +				||(pDrv2605data->work_mode == WORK_SEQ_RTP_ON) +				||(pDrv2605data->work_mode == WORK_SEQ_RTP_OFF) +				||(pDrv2605data->work_mode == WORK_RTP)){ +			hrtimer_cancel(&pDrv2605data->timer); +			cancel_work_sync(&pDrv2605data->vibrator_work); +			vibrator_off(pDrv2605data); +		}else if(pDrv2605data->work_mode == WORK_SEQ_PLAYBACK){ +			pDrv2605data->should_stop = YES; +			cancel_work_sync(&pDrv2605data->vibrator_work); +		}else{ +			printk("%s, err mode=%d \n", __FUNCTION__, pDrv2605data->work_mode); +		} +	} +} + +static int vibrator_get_time(struct timed_output_dev *dev) +{ +	struct drv2605_data *pDrv2605data = container_of(dev, struct drv2605_data, to_dev); + +    if (hrtimer_active(&pDrv2605data->timer)) { +        ktime_t r = hrtimer_get_remaining(&pDrv2605data->timer); +        return ktime_to_ms(r); +    } + +    return 0; +} + +static void vibrator_enable( struct timed_output_dev *dev, int value) +{ +	struct drv2605_data *pDrv2605data = container_of(dev, struct drv2605_data, to_dev); + +    mutex_lock(&pDrv2605data->lock); + +	if(pDrv2605data->vibrator_is_playing){ +		if(pDrv2605data->work_mode == WORK_AUDIO2HAPTIC){ +			setAudioHapticsEnabled(pDrv2605data, NO); +		}else if((pDrv2605data->work_mode == WORK_VIBRATOR) +				||(pDrv2605data->work_mode == WORK_PATTERN_RTP_ON) +				||(pDrv2605data->work_mode == WORK_PATTERN_RTP_OFF) +				||(pDrv2605data->work_mode == WORK_SEQ_RTP_ON) +				||(pDrv2605data->work_mode == WORK_SEQ_RTP_OFF) +				||(pDrv2605data->work_mode == WORK_RTP)){ +			hrtimer_cancel(&pDrv2605data->timer); +			cancel_work_sync(&pDrv2605data->vibrator_work); +			vibrator_off(pDrv2605data); +		}else if(pDrv2605data->work_mode == WORK_SEQ_PLAYBACK){ +			pDrv2605data->should_stop = YES; +			cancel_work_sync(&pDrv2605data->vibrator_work); +		}else{ +			printk("%s, err mode=%d \n", __FUNCTION__, pDrv2605data->work_mode); +		} +	} + +    if (value > 0) { +		if(pDrv2605data->audio_haptics_enabled == NO){ +			wake_lock(&pDrv2605data->wklock); +		} + +		drv2605_change_mode(pDrv2605data, WORK_VIBRATOR, DEV_READY); +		pDrv2605data->vibrator_is_playing = YES; +		switch_set_state(&pDrv2605data->sw_dev, SW_STATE_RTP_PLAYBACK); + +		value = (value>MAX_TIMEOUT)?MAX_TIMEOUT:value; +        hrtimer_start(&pDrv2605data->timer, ns_to_ktime((u64)value * NSEC_PER_MSEC), HRTIMER_MODE_REL); +    } + +	mutex_unlock(&pDrv2605data->lock); +} + +static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) +{ +	struct drv2605_data *pDrv2605data = container_of(timer, struct drv2605_data, timer); +	mutex_lock(&pDrv2605data->lock); +    schedule_work(&pDrv2605data->vibrator_work); +	mutex_unlock(&pDrv2605data->lock); +    return HRTIMER_NORESTART; +} + +static void vibrator_work_routine(struct work_struct *work) +{ +	struct drv2605_data *pDrv2605data = container_of(work, struct drv2605_data, vibrator_work); + +	mutex_lock(&pDrv2605data->lock); +	if((pDrv2605data->work_mode == WORK_VIBRATOR) +		||(pDrv2605data->work_mode == WORK_RTP)){ +		vibrator_off(pDrv2605data); +	}else if(pDrv2605data->work_mode == WORK_SEQ_PLAYBACK){ +		play_effect(pDrv2605data); +	}else if((pDrv2605data->work_mode == WORK_PATTERN_RTP_ON)||(pDrv2605data->work_mode == WORK_PATTERN_RTP_OFF)){ +		play_Pattern_RTP(pDrv2605data); +	}else if((pDrv2605data->work_mode == WORK_SEQ_RTP_ON)||(pDrv2605data->work_mode == WORK_SEQ_RTP_OFF)){ +		play_Seq_RTP(pDrv2605data); +	} +	mutex_unlock(&pDrv2605data->lock); +} + +static int dev2605_open (struct inode * i_node, struct file * filp) +{ +	if(pDRV2605data == NULL){ +		return -ENODEV; +	} + +	filp->private_data = pDRV2605data; +	return 0; +} + +static ssize_t dev2605_read(struct file* filp, char* buff, size_t length, loff_t* offset) +{ +	struct drv2605_data *pDrv2605data = (struct drv2605_data *)filp->private_data; +	int ret = 0; + +	if(pDrv2605data->ReadLen > 0){ +		ret = copy_to_user(buff, pDrv2605data->ReadBuff, pDrv2605data->ReadLen); +		if (ret != 0){ +			printk("%s, copy_to_user err=%d \n", __FUNCTION__, ret); +		}else{ +			ret = pDrv2605data->ReadLen; +		} +		pDrv2605data->ReadLen = 0; +	} + +    return ret; +} + +static bool isforDebug(int cmd){ +	return ((cmd == HAPTIC_CMDID_REG_WRITE) +		||(cmd == HAPTIC_CMDID_REG_READ) +		||(cmd == HAPTIC_CMDID_REG_SETBIT)); +} + +static ssize_t dev2605_write(struct file* filp, const char* buff, size_t len, loff_t* off) +{ +	struct drv2605_data *pDrv2605data = (struct drv2605_data *)filp->private_data; + +    mutex_lock(&pDrv2605data->lock); + +	if(isforDebug(buff[0])){ +	}else{ +		if(pDrv2605data->vibrator_is_playing){ +			if(pDrv2605data->work_mode == WORK_AUDIO2HAPTIC){ +				setAudioHapticsEnabled(pDrv2605data, NO); +			}else if((pDrv2605data->work_mode == WORK_VIBRATOR) +					||(pDrv2605data->work_mode == WORK_PATTERN_RTP_ON) +					||(pDrv2605data->work_mode == WORK_PATTERN_RTP_OFF) +					||(pDrv2605data->work_mode == WORK_SEQ_RTP_ON) +					||(pDrv2605data->work_mode == WORK_SEQ_RTP_OFF) +					||(pDrv2605data->work_mode == WORK_RTP)){ +				hrtimer_cancel(&pDrv2605data->timer); +				cancel_work_sync(&pDrv2605data->vibrator_work); +				vibrator_off(pDrv2605data); +			}else if(pDrv2605data->work_mode == WORK_SEQ_PLAYBACK){ +				pDrv2605data->should_stop = YES; +				cancel_work_sync(&pDrv2605data->vibrator_work); +			}else{ +				printk("%s, err mode=%d \n", __FUNCTION__, pDrv2605data->work_mode); +			} +		} +	} + +    switch(buff[0]) +    { +        case HAPTIC_CMDID_PLAY_SINGLE_EFFECT: +        case HAPTIC_CMDID_PLAY_EFFECT_SEQUENCE: +		{ +            memset(&pDrv2605data->sequence, 0, WAVEFORM_SEQUENCER_MAX); +            if (!copy_from_user(&pDrv2605data->sequence, &buff[1], len - 1)) +            { +				if(pDrv2605data->audio_haptics_enabled == NO){ +					wake_lock(&pDrv2605data->wklock); +				} +				pDrv2605data->should_stop = NO; +				drv2605_change_mode(pDrv2605data, WORK_SEQ_PLAYBACK, DEV_IDLE); +                schedule_work(&pDrv2605data->vibrator_work); +            } +            break; +        } +        case HAPTIC_CMDID_PLAY_TIMED_EFFECT: +        { +            unsigned int value = 0; +            value = buff[2]; +            value <<= 8; +            value |= buff[1]; + +            if (value > 0) +            { +				if(pDrv2605data->audio_haptics_enabled == NO){ +					wake_lock(&pDrv2605data->wklock); +				} +				switch_set_state(&pDrv2605data->sw_dev, SW_STATE_RTP_PLAYBACK); +				pDrv2605data->vibrator_is_playing = YES; +  				value = (value > MAX_TIMEOUT)?MAX_TIMEOUT:value; +				drv2605_change_mode(pDrv2605data, WORK_RTP, DEV_READY); + +				hrtimer_start(&pDrv2605data->timer, ns_to_ktime((u64)value * NSEC_PER_MSEC), HRTIMER_MODE_REL); +            } +            break; +        } + +       case HAPTIC_CMDID_PATTERN_RTP: +        { +			unsigned char strength = 0; + +			pDrv2605data->vibration_time = (int)((((int)buff[2])<<8) | (int)buff[1]); +			pDrv2605data->silience_time = (int)((((int)buff[4])<<8) | (int)buff[3]); +			strength = buff[5]; +			pDrv2605data->repeat_times = buff[6]; + +            if(pDrv2605data->vibration_time > 0){ +				if(pDrv2605data->audio_haptics_enabled == NO){ +					wake_lock(&pDrv2605data->wklock); +				} +				switch_set_state(&pDrv2605data->sw_dev, SW_STATE_RTP_PLAYBACK); +				pDrv2605data->vibrator_is_playing = YES; +                if(pDrv2605data->repeat_times > 0) +					pDrv2605data->repeat_times--; +                if (pDrv2605data->vibration_time > MAX_TIMEOUT) +                    pDrv2605data->vibration_time = MAX_TIMEOUT; +				drv2605_change_mode(pDrv2605data, WORK_PATTERN_RTP_ON, DEV_READY); +				drv2605_set_rtp_val(pDrv2605data, strength); + +                hrtimer_start(&pDrv2605data->timer, ns_to_ktime((u64)pDrv2605data->vibration_time * NSEC_PER_MSEC), HRTIMER_MODE_REL); +            } +            break; +        } + +		case HAPTIC_CMDID_RTP_SEQUENCE: +		{ +            memset(&pDrv2605data->RTPSeq, 0, sizeof(struct RTP_Seq)); +			if(((len-1)%2) == 0){ +				pDrv2605data->RTPSeq.RTPCounts = (len-1)/2; +				if((pDrv2605data->RTPSeq.RTPCounts <= MAX_RTP_SEQ)&&(pDrv2605data->RTPSeq.RTPCounts>0)){ +					if(copy_from_user(pDrv2605data->RTPSeq.RTPData, &buff[1], pDrv2605data->RTPSeq.RTPCounts*2) != 0){ +						printk("%s, rtp_seq copy seq err\n", __FUNCTION__); +						break; +					} + +					if(pDrv2605data->audio_haptics_enabled == NO){ +						wake_lock(&pDrv2605data->wklock); +					} +					switch_set_state(&pDrv2605data->sw_dev, SW_STATE_RTP_PLAYBACK); +					drv2605_change_mode(pDrv2605data, WORK_SEQ_RTP_OFF, DEV_IDLE); +					schedule_work(&pDrv2605data->vibrator_work); +				}else{ +					printk("%s, rtp_seq count error,maximum=%d\n", __FUNCTION__,MAX_RTP_SEQ); +				} +			}else{ +				printk("%s, rtp_seq len error\n", __FUNCTION__); +			} +			break; +		} + +        case HAPTIC_CMDID_STOP: +        { +            break; +        } + +        case HAPTIC_CMDID_AUDIOHAPTIC_ENABLE: +        { +			if(pDrv2605data->audio_haptics_enabled == NO){ +				wake_lock(&pDrv2605data->wklock); +			} +			pDrv2605data->audio_haptics_enabled = YES; +			setAudioHapticsEnabled(pDrv2605data, YES); +            break; +        } + +        case HAPTIC_CMDID_AUDIOHAPTIC_DISABLE: +        { +			if(pDrv2605data->audio_haptics_enabled == YES){ +				pDrv2605data->audio_haptics_enabled = NO; +				wake_unlock(&pDrv2605data->wklock); +			} +            break; +        } + +		case HAPTIC_CMDID_REG_READ: +		{ +			if(len == 2){ +				pDrv2605data->ReadLen = 1; +				pDrv2605data->ReadBuff[0] = drv2605_reg_read(pDrv2605data, buff[1]); +			}else if(len == 3){ +				pDrv2605data->ReadLen = (buff[2]>MAX_READ_BYTES)?MAX_READ_BYTES:buff[2]; +				drv2605_bulk_read(pDrv2605data, buff[1], pDrv2605data->ReadLen, pDrv2605data->ReadBuff); +			}else{ +				printk("%s, reg_read len error\n", __FUNCTION__); +			} +			break; +		} + +		case HAPTIC_CMDID_REG_WRITE: +		{ +			if((len-1) == 2){ +				drv2605_reg_write(pDrv2605data, buff[1], buff[2]); +			}else if((len-1)>2){ +				unsigned char *data = (unsigned char *)kzalloc(len-2, GFP_KERNEL); +				if(data != NULL){ +					if(copy_from_user(data, &buff[2], len-2) != 0){ +						printk("%s, reg copy err\n", __FUNCTION__); +					}else{ +						drv2605_bulk_write(pDrv2605data, buff[1], len-2, data); +					} +					kfree(data); +				} +			}else{ +				printk("%s, reg_write len error\n", __FUNCTION__); +			} +			break; +		} + +		case HAPTIC_CMDID_REG_SETBIT: +		{ +			int i=1; +			for(i=1; i< len; ){ +				drv2605_set_bits(pDrv2605data, buff[i], buff[i+1], buff[i+2]); +				i += 3; +			} +			break; +		} +    default: +		printk("%s, unknown HAPTIC cmd\n", __FUNCTION__); +      break; +    } + +    mutex_unlock(&pDrv2605data->lock); + +    return len; +} + + +static struct file_operations fops = +{ +	.open = dev2605_open, +    .read = dev2605_read, +    .write = dev2605_write, +}; + +static int Haptics_init(struct drv2605_data *pDrv2605data) +{ +    int reval = -ENOMEM; + +    pDrv2605data->version = MKDEV(0,0); +    reval = alloc_chrdev_region(&pDrv2605data->version, 0, 1, HAPTICS_DEVICE_NAME); +    if (reval < 0) +    { +        printk(KERN_ALERT"drv2605: error getting major number %d\n", reval); +        goto fail0; +    } + +    pDrv2605data->class = class_create(THIS_MODULE, HAPTICS_DEVICE_NAME); +    if (!pDrv2605data->class) +    { +        printk(KERN_ALERT"drv2605: error creating class\n"); +        goto fail1; +    } + +    pDrv2605data->device = device_create(pDrv2605data->class, NULL, pDrv2605data->version, NULL, HAPTICS_DEVICE_NAME); +    if (!pDrv2605data->device) +    { +        printk(KERN_ALERT"drv2605: error creating device 2605\n"); +        goto fail2; +    } + +    cdev_init(&pDrv2605data->cdev, &fops); +    pDrv2605data->cdev.owner = THIS_MODULE; +    pDrv2605data->cdev.ops = &fops; +    reval = cdev_add(&pDrv2605data->cdev, pDrv2605data->version, 1); +    if (reval) +    { +        printk(KERN_ALERT"drv2605: fail to add cdev\n"); +        goto fail3; +    } + +	pDrv2605data->sw_dev.name = "haptics"; +	reval = switch_dev_register(&pDrv2605data->sw_dev); +	if (reval < 0) { +		printk(KERN_ALERT"drv2605: fail to register switch\n"); +		goto fail4; +	} + +	pDrv2605data->to_dev.name = "vibrator"; +	pDrv2605data->to_dev.get_time = vibrator_get_time; +	pDrv2605data->to_dev.enable = vibrator_enable; + +    if (timed_output_dev_register(&(pDrv2605data->to_dev)) < 0) +    { +        printk(KERN_ALERT"drv2605: fail to create timed output dev\n"); +        goto fail3; +    } + +    hrtimer_init(&pDrv2605data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); +    pDrv2605data->timer.function = vibrator_timer_func; +    INIT_WORK(&pDrv2605data->vibrator_work, vibrator_work_routine); + +    wake_lock_init(&pDrv2605data->wklock, WAKE_LOCK_SUSPEND, "vibrator"); +    mutex_init(&pDrv2605data->lock); + +    return 0; + +fail4: +	switch_dev_unregister(&pDrv2605data->sw_dev); +fail3: +	device_destroy(pDrv2605data->class, pDrv2605data->version); +fail2: +    class_destroy(pDrv2605data->class); +fail1: +    unregister_chrdev_region(pDrv2605data->version, 1); +fail0: +    return reval; +} + +static void dev_init_platform_data(struct drv2605_data *pDrv2605data) +{ +	struct drv2605_platform_data *pDrv2605Platdata = &pDrv2605data->PlatData; +	struct actuator_data actuator = pDrv2605Platdata->actuator; +	struct audio2haptics_data a2h = pDrv2605Platdata->a2h; +	unsigned char temp = 0; + +	drv2605_select_library(pDrv2605data, actuator.g_effect_bank); + +	//OTP memory saves data from 0x16 to 0x1a +	if(pDrv2605data->OTP == 0) { +		if(actuator.rated_vol != 0){ +			drv2605_reg_write(pDrv2605data, RATED_VOLTAGE_REG, actuator.rated_vol); +		}else{ +			printk("%s, ERROR Rated ZERO\n", __FUNCTION__); +		} + +		if(actuator.over_drive_vol != 0){ +			drv2605_reg_write(pDrv2605data, OVERDRIVE_CLAMP_VOLTAGE_REG, actuator.over_drive_vol); +		}else{ +			printk("%s, ERROR OverDriveVol ZERO\n", __FUNCTION__); +		} + +		drv2605_set_bits(pDrv2605data, +						FEEDBACK_CONTROL_REG, +						FEEDBACK_CONTROL_DEVICE_TYPE_MASK, +						(actuator.device_type == LRA)?FEEDBACK_CONTROL_MODE_LRA:FEEDBACK_CONTROL_MODE_ERM); +	}else{ +		printk("%s, OTP programmed\n", __FUNCTION__); +	} + +	if(pDrv2605Platdata->loop == OPEN_LOOP){ +		temp = BIDIR_INPUT_BIDIRECTIONAL; +	}else{ +		if(pDrv2605Platdata->BIDIRInput == UniDirectional){ +			temp = BIDIR_INPUT_UNIDIRECTIONAL; +		}else{ +			temp = BIDIR_INPUT_BIDIRECTIONAL; +		} +	} + +	if(actuator.device_type == LRA){ +		unsigned char DriveTime = 5*(1000 - actuator.LRAFreq)/actuator.LRAFreq; +		drv2605_set_bits(pDrv2605data, +				Control1_REG, +				Control1_REG_DRIVE_TIME_MASK, +				DriveTime); +		printk("%s, LRA = %d, DriveTime=0x%x\n", __FUNCTION__, actuator.LRAFreq, DriveTime); +	} + +	drv2605_set_bits(pDrv2605data, +				Control2_REG, +				Control2_REG_BIDIR_INPUT_MASK, +				temp); + +	if((pDrv2605Platdata->loop == OPEN_LOOP)&&(actuator.device_type == LRA)) +	{ +		temp = LRA_OpenLoop_Enabled; +	} +	else if((pDrv2605Platdata->loop == OPEN_LOOP)&&(actuator.device_type == ERM)) +	{ +		temp = ERM_OpenLoop_Enabled; +	} +	else +	{ +		temp = ERM_OpenLoop_Disable|LRA_OpenLoop_Disable; +	} + +	if((pDrv2605Platdata->loop == CLOSE_LOOP) &&(pDrv2605Platdata->BIDIRInput == UniDirectional)) +	{ +		temp |= RTP_FORMAT_UNSIGNED; +		drv2605_reg_write(pDrv2605data, REAL_TIME_PLAYBACK_REG, 0xff); +	} +	else +	{ +		if(pDrv2605Platdata->RTPFormat == Signed) +		{ +			temp |= RTP_FORMAT_SIGNED; +			drv2605_reg_write(pDrv2605data, REAL_TIME_PLAYBACK_REG, 0x7f); +		} +		else +		{ +			temp |= RTP_FORMAT_UNSIGNED; +			drv2605_reg_write(pDrv2605data, REAL_TIME_PLAYBACK_REG, 0xff); +		} +	} +	drv2605_set_bits(pDrv2605data, +					Control3_REG, +					Control3_REG_LOOP_MASK|Control3_REG_FORMAT_MASK, +					temp); + +	//for audio to haptics +	if(pDrv2605Platdata->GpioTrigger == 0)	//not used as external trigger +	{ +		drv2605_reg_write(pDrv2605data, AUDIO_HAPTICS_MIN_INPUT_REG, a2h.a2h_min_input); +		drv2605_reg_write(pDrv2605data, AUDIO_HAPTICS_MAX_INPUT_REG, a2h.a2h_max_input); +		drv2605_reg_write(pDrv2605data, AUDIO_HAPTICS_MIN_OUTPUT_REG, a2h.a2h_min_output); +		drv2605_reg_write(pDrv2605data, AUDIO_HAPTICS_MAX_OUTPUT_REG, a2h.a2h_max_output); +	} +} + +static int dev_auto_calibrate(struct drv2605_data *pDrv2605data) +{ +	int err = 0, status=0; + +	drv2605_change_mode(pDrv2605data, WORK_CALIBRATION, DEV_READY); +	drv2605_set_go_bit(pDrv2605data, GO); + +	/* Wait until the procedure is done */ +	drv2605_poll_go_bit(pDrv2605data); +	/* Read status */ +	status = drv2605_reg_read(pDrv2605data, STATUS_REG); + +	printk("%s, calibration status =0x%x\n", __FUNCTION__, status); + +	/* Read calibration results */ +	drv2605_reg_read(pDrv2605data, AUTO_CALI_RESULT_REG); +	drv2605_reg_read(pDrv2605data, AUTO_CALI_BACK_EMF_RESULT_REG); +	drv2605_reg_read(pDrv2605data, FEEDBACK_CONTROL_REG); + +	return err; +} + +static struct regmap_config drv2605_i2c_regmap = { +	.reg_bits = 8, +	.val_bits = 8, +	.cache_type = REGCACHE_NONE, +}; + +static int drv2605_probe(struct i2c_client* client, const struct i2c_device_id* id) +{ +	struct drv2605_data *pDrv2605data; +	struct drv2605_platform_data *pDrv2605Platdata = client->dev.platform_data; + +	int err = 0; +	int status = 0; + +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) +	{ +		printk(KERN_ERR"%s:I2C check failed\n", __FUNCTION__); +		return -ENODEV; +	} + +	pDrv2605data = devm_kzalloc(&client->dev, sizeof(struct drv2605_data), GFP_KERNEL); +	if (pDrv2605data == NULL){ +		printk(KERN_ERR"%s:no memory\n", __FUNCTION__); +		return -ENOMEM; +	} + +	pDrv2605data->regmap = devm_regmap_init_i2c(client, &drv2605_i2c_regmap); +	if (IS_ERR(pDrv2605data->regmap)) { +		err = PTR_ERR(pDrv2605data->regmap); +		printk(KERN_ERR"%s:Failed to allocate register map: %d\n",__FUNCTION__,err); +		return err; +	} + +	memcpy(&pDrv2605data->PlatData, pDrv2605Platdata, sizeof(struct drv2605_platform_data)); +	i2c_set_clientdata(client,pDrv2605data); + +	if(pDrv2605data->PlatData.GpioTrigger){ +		err = gpio_request(pDrv2605data->PlatData.GpioTrigger,HAPTICS_DEVICE_NAME"Trigger"); +		if(err < 0){ +			printk(KERN_ERR"%s: GPIO request Trigger error\n", __FUNCTION__); +			goto exit_gpio_request_failed; +		} +	} + +	if(pDrv2605data->PlatData.GpioEnable){ +		err = gpio_request(pDrv2605data->PlatData.GpioEnable,HAPTICS_DEVICE_NAME"Enable"); +		if(err < 0){ +			printk(KERN_ERR"%s: GPIO request enable error\n", __FUNCTION__); +			goto exit_gpio_request_failed; +		} + +	    /* Enable power to the chip */ +	    gpio_direction_output(pDrv2605data->PlatData.GpioEnable, 1); + +	    /* Wait 30 us */ +	    udelay(30); +	} + +	err = drv2605_reg_read(pDrv2605data, STATUS_REG); +	if(err < 0){ +		printk("%s, i2c bus fail (%d)\n", __FUNCTION__, err); +		goto exit_gpio_request_failed; +	}else{ +		printk("%s, i2c status (0x%x)\n", __FUNCTION__, err); +		status = err; +	} +	/* Read device ID */ +	pDrv2605data->device_id = (status & DEV_ID_MASK); +	switch (pDrv2605data->device_id) +	{ +		case DRV2605_VER_1DOT1: +		printk("drv2605 driver found: drv2605 v1.1.\n"); +		break; +		case DRV2605_VER_1DOT0: +		printk("drv2605 driver found: drv2605 v1.0.\n"); +		break; +		case DRV2604: +		printk(KERN_ALERT"drv2605 driver found: drv2604.\n"); +		break; +		default: +		printk(KERN_ERR"drv2605 driver found: unknown.\n"); +		break; +	} + +	if((pDrv2605data->device_id != DRV2605_VER_1DOT1) +		&&(pDrv2605data->device_id != DRV2605_VER_1DOT0)){ +		printk("%s, status(0x%x),device_id(%d) fail\n", +			__FUNCTION__, status, pDrv2605data->device_id); +		goto exit_gpio_request_failed; +	} + +	drv2605_change_mode(pDrv2605data, WORK_IDLE, DEV_READY); +	schedule_timeout_interruptible(msecs_to_jiffies(STANDBY_WAKE_DELAY)); + +	pDrv2605data->OTP = drv2605_reg_read(pDrv2605data, AUTOCAL_MEM_INTERFACE_REG) & AUTOCAL_MEM_INTERFACE_REG_OTP_MASK; + +	dev_init_platform_data(pDrv2605data); + +	if(pDrv2605data->OTP == 0){ +		err = dev_auto_calibrate(pDrv2605data); +		if(err < 0){ +			printk("%s, ERROR, calibration fail\n",	__FUNCTION__); +		} +	} + +    /* Put hardware in standby */ +    drv2605_change_mode(pDrv2605data, WORK_IDLE, DEV_STANDBY); + +    Haptics_init(pDrv2605data); + +	pDRV2605data = pDrv2605data; +    printk("drv2605 probe succeeded\n"); + +    return 0; + +exit_gpio_request_failed: +	if(pDrv2605data->PlatData.GpioTrigger){ +		gpio_free(pDrv2605data->PlatData.GpioTrigger); +	} + +	if(pDrv2605data->PlatData.GpioEnable){ +		gpio_free(pDrv2605data->PlatData.GpioEnable); +	} + +    printk(KERN_ERR"%s failed, err=%d\n",__FUNCTION__, err); +	return err; +} + +static int drv2605_remove(struct i2c_client* client) +{ +	struct drv2605_data *pDrv2605data = i2c_get_clientdata(client); + +    device_destroy(pDrv2605data->class, pDrv2605data->version); +    class_destroy(pDrv2605data->class); +    unregister_chrdev_region(pDrv2605data->version, 1); + +	if(pDrv2605data->PlatData.GpioTrigger) +		gpio_free(pDrv2605data->PlatData.GpioTrigger); + +	if(pDrv2605data->PlatData.GpioEnable) +		gpio_free(pDrv2605data->PlatData.GpioEnable); + +    printk(KERN_ALERT"drv2605 remove"); + +    return 0; +} + +static struct i2c_device_id drv2605_id_table[] = +{ +    { HAPTICS_DEVICE_NAME, 0 }, +    {} +}; +MODULE_DEVICE_TABLE(i2c, drv2605_id_table); + +static struct i2c_driver drv2605_driver = +{ +    .driver = { +        .name = HAPTICS_DEVICE_NAME, +		.owner = THIS_MODULE, +    }, +    .id_table = drv2605_id_table, +    .probe = drv2605_probe, +    .remove = drv2605_remove, +}; + +static int __init drv2605_init(void) +{ +	return i2c_add_driver(&drv2605_driver); +} + +static void __exit drv2605_exit(void) +{ +	i2c_del_driver(&drv2605_driver); +} + +module_init(drv2605_init); +module_exit(drv2605_exit); + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("Driver for "HAPTICS_DEVICE_NAME); diff --git a/include/linux/haptics/drv2605.h b/include/linux/haptics/drv2605.h new file mode 100644 index 00000000000..f448c657755 --- /dev/null +++ b/include/linux/haptics/drv2605.h @@ -0,0 +1,458 @@ +#ifndef __DRV2605_H__ +#define __DRV2605_H__ +/* +** ============================================================================= +** Copyright (c)2014  Texas Instruments Inc. +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. +** +** File: +**     drv2605.h +** +** Description: +**     Header file for drv2605.c +** +** ============================================================================= +*/ + +#include <linux/switch.h> +#include <linux/regmap.h> +#include <linux/timer.h> +#include <linux/workqueue.h> +#include <../../../drivers/staging/android/timed_output.h> +#include <linux/hrtimer.h> +#include <linux/wakelock.h> +#include <linux/mutex.h> +#include <linux/semaphore.h> +#include <linux/cdev.h> + +#define HAPTICS_DEVICE_NAME "drv2605" + +#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 GO_BIT_POLL_INTERVAL    15 +#define STANDBY_WAKE_DELAY      1 +#define WAKE_STANDBY_DELAY      3 + +/* 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_REG_WRITE  	0x09 +#define HAPTIC_CMDID_REG_READ   	0x0a +#define HAPTIC_CMDID_REG_SETBIT  	0x0b + +#define HAPTIC_CMDID_PATTERN_RTP      0x0c +#define HAPTIC_CMDID_RTP_SEQUENCE	  0x0d +#define HAPTIC_CMDID_GET_EFFECT_COUNT		0x10 +#define HAPTIC_CMDID_UPDATE_FIRMWARE		0x11 +#define HAPTIC_CMDID_READ_FIRMWARE			0x12 +#define HAPTIC_CMDID_STOP                   0xFF + +/* +** Go +*/ +#define GO_REG 	0x0C +#define GO_MASK	0x01 +#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_VER_1DOT1 (3 << 5) +#define DRV2605_VER_1DOT0 (5 << 5) +#define DRV2604 (4 << 5) +#define DRV2604L (6 << 5) +#define DRV2605L (7 << 5) + +/* +** Mode +*/ +#define MODE_REG            0x01 +#define MODE_STANDBY_MASK           0x40 +#define MODE_STANDBY        0x40 +#define MODE_RESET                  0x80 +#define DRV2605_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 + +/* +** Real Time Playback +*/ +#define REAL_TIME_PLAYBACK_REG		0x02 + +/* +** Library Selection +*/ +#define LIBRARY_SELECTION_REG       0x03 +#define LIBRARY_SELECTION_DEFAULT   0x00 +#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 +*/ +#define FEEDBACK_CONTROL_REG        0x1A +#define FEEDBACK_CONTROL_DEVICE_TYPE_MASK        0x80 +#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) + +/* +** Control1 +*/ +#define Control1_REG            	0x1B +#define Control1_REG_AC_COUPLE_MASK	0x20 +#define Control1_REG_DRIVE_TIME_MASK	0x1f + +#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      0x13 +#define AUDIOHAPTIC_DRIVE_TIME  0x13 + +/* +** Control2 +*/ +#define Control2_REG            0x1C +#define Control2_REG_BIDIR_INPUT_MASK   0x80 + +#define	BIDIR_INPUT_UNIDIRECTIONAL	(0<<7) +#define	BIDIR_INPUT_BIDIRECTIONAL	(1<<7) +#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 BRAKE_STABLIZER   (1<<6) +#define BIDIRECT_INPUT             (1 << 7) // default + +/* +** Control3 +*/ +#define Control3_REG 0x1D +#define Control3_REG_LOOP_MASK		0x21 +#define Control3_REG_PWMANALOG_MASK	0x02 +#define Control3_REG_FORMAT_MASK	0x08 +#define INPUT_PWM               (0 << 1) // default +#define INPUT_ANALOG            (1 << 1) +#define ERM_OpenLoop_Enabled    (1 << 5) +#define ERM_OpenLoop_Disable    (0 << 5) +#define LRA_OpenLoop_Enabled    (1 << 0) +#define LRA_OpenLoop_Disable    (0 << 0) +#define RTP_FORMAT_SIGNED	    (0 << 3) +#define RTP_FORMAT_UNSIGNED	    (1 << 3) +#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_MEM_INTERFACE_REG_OTP_MASK   0x04 + +#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 + +//reset values +#define AUDIO_HAPTICS_MIN_INPUT_VOLTAGE     0x19 +#define AUDIO_HAPTICS_MAX_INPUT_VOLTAGE     0xff +#define AUDIO_HAPTICS_MIN_OUTPUT_VOLTAGE    0x19 +#define AUDIO_HAPTICS_MAX_OUTPUT_VOLTAGE    0xFF + +#define MAX_TIMEOUT 	10000 /* 10s */ +#define	MAX_READ_BYTES	0xff + +#define SW_STATE_IDLE				0x00 +#define SW_STATE_AUDIO2HAPTIC			0x01 +#define SW_STATE_SEQUENCE_PLAYBACK		0x02 +#define SW_STATE_RTP_PLAYBACK			0x04 + +#define DEV_IDLE	                0 // default +#define DEV_STANDBY					1 +#define DEV_READY					2 + +#define	WORK_IDLE					0x00 +#define WORK_AUDIO2HAPTIC	      	0x05 +#define WORK_RTP			      	0x06 +#define WORK_CALIBRATION	      	0x07 +#define WORK_VIBRATOR		      	0x08 +#define	WORK_PATTERN_RTP_ON			0x09 +#define WORK_PATTERN_RTP_OFF      	0x0a +#define WORK_SEQ_RTP_ON		      	0x0b +#define WORK_SEQ_RTP_OFF    	  	0x0c +#define WORK_SEQ_PLAYBACK    	  	0x0d + +#define YES 1 +#define NO  0 + +enum actuator_type { +	ERM, +	LRA +}; + +enum loop_type { +	OPEN_LOOP, +	CLOSE_LOOP +}; + +enum RTP_Format { +	Signed, +	Unsigned +}; + +enum BIDIR_Input { +	UniDirectional, +	BiDirectional +}; + +struct actuator_data { +	enum actuator_type device_type; +	unsigned char   g_effect_bank; +	unsigned char 	rated_vol; +	unsigned char 	over_drive_vol; +	unsigned char   LRAFreq; +}; + +struct audio2haptics_data { +	unsigned char 	a2h_min_input; +	unsigned char 	a2h_max_input; +	unsigned char 	a2h_min_output; +	unsigned char 	a2h_max_output; +}; + +struct drv2605_platform_data { +	int				GpioEnable; +	int 			GpioTrigger; +	enum loop_type	loop; +	enum RTP_Format RTPFormat; +	enum BIDIR_Input BIDIRInput; +	struct actuator_data actuator; +	struct audio2haptics_data a2h; +}; + +#define	MAX_RTP_SEQ		16 + +struct RTP_Seq{ +	unsigned short RTPData[MAX_RTP_SEQ]; //RTPTime<<8||RTPAmp +	int RTPCounts; +	int RTPindex; +}; + +struct drv2605_data { +	struct drv2605_platform_data PlatData; +	int	device_id; + +	struct regmap *regmap; + +	struct class* class; +	struct device* device; +	dev_t version; +	struct semaphore sem; +	struct cdev cdev; +	struct switch_dev sw_dev; +	volatile int audio_haptics_enabled; +	volatile int vibrator_is_playing; +	char ReadBuff[MAX_READ_BYTES]; +	int ReadLen; + +	int vibration_time; +	int silience_time; +	char repeat_times; +	volatile char work_mode; +	char dev_mode; + +	struct RTP_Seq RTPSeq; + +	int OTP; + +    struct wake_lock wklock; +    struct hrtimer timer; +    struct mutex lock; +    struct work_struct vibrator_work; +    unsigned char sequence[WAVEFORM_SEQUENCER_MAX]; +    volatile int should_stop; +	struct timed_output_dev to_dev; +}; + +#endif |