summaryrefslogtreecommitdiff
path: root/drivers/input/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/misc')
-rw-r--r--drivers/input/misc/Kconfig8
-rw-r--r--drivers/input/misc/Makefile2
-rw-r--r--drivers/input/misc/hall_switch.c269
-rw-r--r--drivers/input/misc/vibrator/Kconfig13
-rw-r--r--drivers/input/misc/vibrator/Makefile1
-rw-r--r--drivers/input/misc/vibrator/drv2605l.c1125
-rw-r--r--drivers/input/misc/vibrator/drv2605l.h440
7 files changed, 1858 insertions, 0 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index dd98d8c8fd1f..2b71d0f4e3bc 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -844,4 +844,12 @@ config INPUT_STMVL53L0
To compile this driver as a module, choose M here: the
module will be called stmvl53l0.
+config HALL_SWITCH
+ tristate "hall sensor for ZUK devices"
+ default y
+ help
+ If you say yes here, you get support for hall sensor
+
endif
+
+source "drivers/input/misc/vibrator/Kconfig"
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 44c026abcb6f..1401ab288f18 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -81,3 +81,5 @@ obj-$(CONFIG_INPUT_YEALINK) += yealink.o
obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
obj-$(CONFIG_INPUT_PIXART_OTS_PAT9125_SWITCH) += ots_pat9125/
obj-$(CONFIG_INPUT_STMVL53L0) += vl53L0/
+obj-$(CONFIG_DRV2605L_HAPTICS) += vibrator/
+obj-$(CONFIG_HALL_SWITCH) += hall_switch.o
diff --git a/drivers/input/misc/hall_switch.c b/drivers/input/misc/hall_switch.c
new file mode 100644
index 000000000000..fb186c3be77f
--- /dev/null
+++ b/drivers/input/misc/hall_switch.c
@@ -0,0 +1,269 @@
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/bitops.h>
+
+#include <linux/platform_device.h>
+
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+
+
+static volatile int key_debug = 5;
+#define HALL_GPIO 124
+
+#define SSC_VDD_2P85_HPM_LOAD 600000 //uA
+
+
+struct hall_switch_data{
+ struct regulator *vdd;
+ struct regulator *ssc_vdd;
+ struct input_dev *input_dev;
+ struct delayed_work hall_work;
+ struct workqueue_struct *hall_workqueue;
+ int irq_gpio;
+ int hall_irq;
+ int hall_gpio_val;
+ struct device *dev;
+};
+static struct hall_switch_data *hall_data = NULL;
+static irqreturn_t misc_hall_irq(int irq, void *data)
+{
+ struct hall_switch_data *hall_data = data;
+ int gpio_value;
+ //int ret;
+
+ if(hall_data == NULL)
+ return 0;
+ disable_irq_nosync(hall_data->hall_irq);
+ gpio_value = gpio_get_value(HALL_GPIO);
+ if(gpio_value){
+ /*----hall far----*/
+ if(key_debug == 5)
+ printk("hall-switch %d,report: far\n",HALL_GPIO);
+ input_event(hall_data->input_dev, EV_SW, SW_LID, 0);
+ input_sync(hall_data->input_dev);
+ }else{
+ /*----hall near----*/
+ if(key_debug == 5)
+ printk("hall-switch %d,report: near!!!\n",HALL_GPIO);
+ input_event(hall_data->input_dev, EV_SW, SW_LID, 1);
+ input_sync(hall_data->input_dev);
+ }
+ enable_irq(hall_data->hall_irq);
+ return IRQ_HANDLED;
+}
+
+static ssize_t hall_irq_gpio_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int tmp = gpio_get_value(HALL_GPIO);
+ return sprintf(buf, "%s\n", tmp==0?"0":"1");
+}
+
+static DEVICE_ATTR(hall_int_gpio, 0444, hall_irq_gpio_show, NULL);
+
+static struct attribute *hall_attributes[] = {
+ &dev_attr_hall_int_gpio.attr,
+ NULL,
+};
+
+static struct attribute_group hall_attr_group = {
+ .attrs = hall_attributes,
+};
+
+static int hall_probe(struct platform_device *pdev)
+{
+ static int is_probed = 0; /* Flag used to avoid double probe. */
+ int retval = 0;
+
+ int err = 0;
+ struct device_node *np = pdev->dev.of_node;
+
+ if (is_probed) {
+ printk("%s: Already probed succesfully, ignored\n", __FUNCTION__);
+ return 0;
+ }
+
+ hall_data = kzalloc(sizeof(struct hall_switch_data), GFP_KERNEL);
+ if (!hall_data){
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ /*hall_data->vdd = regulator_get(&pdev->dev,"vdd");
+ if (!IS_ERR(hall_data->vdd)) {
+ printk("%s,vdd is correct",__func__);
+ err = regulator_enable(hall_data->vdd);
+ if (err) {
+ printk("%s,Regulator vdd enable failed ret=%d\n", __func__,err);
+ }
+ }
+
+ hall_data->ssc_vdd = regulator_get(&pdev->dev,"ssc");
+ if (!IS_ERR(hall_data->ssc_vdd)) {
+ printk("%s,ssc_vdd is correct",__func__);
+ err = regulator_enable(hall_data->ssc_vdd);
+ if (err) {
+ printk("%s,Regulator ssc_vdd enable failed ret=%d\n", __func__,err);
+ }
+ }*/
+
+#if 1
+ hall_data->ssc_vdd = devm_regulator_get(&pdev->dev, "ssc_vdd");
+ if (IS_ERR(hall_data->ssc_vdd)) {
+ pr_err("%s - ssc_vdd regulator_get fail\n", __func__);
+ err = -ENODEV;
+ }
+
+ err = regulator_set_load(hall_data->ssc_vdd, SSC_VDD_2P85_HPM_LOAD);
+ if (err < 0) {
+ pr_err("%s:Unable to set current of ssc_vdd\n",__func__);
+ }
+
+ err = regulator_enable(hall_data->ssc_vdd);
+ if (err) {
+ pr_err("%s - enable ssc_vdd failed, err=%d\n",
+ __func__, err);
+ }
+
+
+ usleep_range(1000, 1100);
+
+#endif
+
+ /*----Register to Input Device----*/
+ hall_data->input_dev = input_allocate_device();
+ if (hall_data->input_dev == NULL){
+ err = -ENOMEM;
+ printk("hall-switch: Failed to allocate input device!!! \n");
+ goto exit_kfree;
+ }
+
+ hall_data->input_dev->name = "hall-switch";
+
+ set_bit(EV_SYN, hall_data->input_dev->evbit);
+ set_bit(EV_SW, hall_data->input_dev->evbit);
+ set_bit(EV_ABS, hall_data->input_dev->evbit);
+
+ set_bit(SW_LID, hall_data->input_dev->swbit);
+ input_set_capability(hall_data->input_dev, EV_SW, SW_LID);
+
+ /*set_bit(KEY_SPORT_B, hall_data->input_dev->keybit);
+ input_set_capability(hall_data->input_dev, EV_KEY, KEY_SPORT_B);
+ set_bit(KEY_SHOP_B, hall_data->input_dev->keybit);
+ input_set_capability(hall_data->input_dev, EV_KEY, KEY_SHOP_B);*/
+
+ retval = input_register_device(hall_data->input_dev);
+ if(retval){
+ printk("hall-switch: Failed to register input device!!!\n");
+ goto exit_register_input;
+ }
+
+ hall_data->irq_gpio = of_get_named_gpio(np, "shenqi,hall-irq-gpio", 0);
+ if (hall_data->irq_gpio < 0) {
+ pr_err("failed to get hall's \"shenqi,hall-irq-gpio\"\n");
+ goto exit_enable_irq;
+ }
+
+ retval = gpio_request(hall_data->irq_gpio, "hall_gpio");
+ if (retval) {
+ printk("hall-switch: irq gpio %d,request failed\n",hall_data->irq_gpio);
+ goto exit_enable_irq;
+ }
+ retval = gpio_direction_input(hall_data->irq_gpio);
+ if (retval) {
+ printk("hall-switch: irq gpio %d,direction set failed\n",hall_data->irq_gpio);
+ goto exit_free_gpio;
+ }
+
+ hall_data->hall_irq = gpio_to_irq(hall_data->irq_gpio);
+ retval = request_threaded_irq( hall_data->hall_irq, NULL, misc_hall_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "misc_hall_irq", hall_data);
+ if(retval < 0){
+ printk("hall-switch: Failed to create hall irq thread!!!i%d\n",hall_data->hall_irq);
+ goto exit_free_gpio;
+ }
+ enable_irq_wake(hall_data->hall_irq);
+
+ retval = sysfs_create_group(&pdev->dev.kobj, &hall_attr_group);
+ if(retval) {
+ printk(KERN_ERR "%s: Failed to create sysfs\n", __FUNCTION__);
+ }
+ printk("%s sysfs_create_group sucess\n", __func__);
+
+ is_probed = 1;
+ return retval;
+exit_free_gpio:
+ gpio_free(HALL_GPIO);
+exit_enable_irq:
+ input_unregister_device(hall_data->input_dev);
+
+exit_register_input:
+ input_free_device(hall_data->input_dev);
+ hall_data->input_dev = NULL;
+
+exit_kfree:
+ kfree(hall_data);
+exit:
+ return err;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id hall_match_table[] = {
+ { .compatible = "shenqi,hall_switch",},
+ { },
+};
+#else
+#define hall_match_table NULL
+#endif
+
+static int hall_prepare(struct device *dev)
+{
+ /*int err = 0;
+ err = regulator_enable(hall_data->ssc_vdd);
+ if (err) {
+ printk("%s,Regulator ssc_vdd enable failed ret=%d\n", __func__,err);
+ }*/
+ return 0;
+}
+
+static void hall_complete(struct device *dev)
+{
+ /*int err = 0;
+ err = regulator_disable(hall_data->ssc_vdd);
+ if (err) {
+ printk("%s,Regulator ssc_vdd disable failed ret=%d\n", __func__,err);
+ }*/
+}
+
+static const struct dev_pm_ops hall_pm = {
+ .prepare = hall_prepare,
+ .complete = hall_complete
+};
+
+static struct platform_driver msm_hall_driver = {
+ .probe = hall_probe,
+ .driver = {
+ .name = "msm_hall_switch",
+ .owner = THIS_MODULE,
+ .of_match_table = hall_match_table,
+ .pm = &hall_pm,
+ },
+};
+
+static int __init hall_init(void)
+{
+ return platform_driver_register(&msm_hall_driver);
+}
+
+module_init(hall_init);
+MODULE_DESCRIPTION("Hall switch sensor driver");
+MODULE_LICENSE("GPL"); \ No newline at end of file
diff --git a/drivers/input/misc/vibrator/Kconfig b/drivers/input/misc/vibrator/Kconfig
new file mode 100644
index 000000000000..3a7bf6ecfc8d
--- /dev/null
+++ b/drivers/input/misc/vibrator/Kconfig
@@ -0,0 +1,13 @@
+menu "zuk z2pro vibrator support"
+
+config DRV2605L_HAPTICS
+ tristate "TI DRV260X haptics support"
+ depends on I2C && GPIOLIB
+ select REGMAP_I2C
+ help
+ Say Y to enable support for the TI DRV260X haptics driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called drv260x-haptics.
+
+endmenu
diff --git a/drivers/input/misc/vibrator/Makefile b/drivers/input/misc/vibrator/Makefile
new file mode 100644
index 000000000000..2e8c80845ebb
--- /dev/null
+++ b/drivers/input/misc/vibrator/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DRV2605L_HAPTICS) += drv2605l.o
diff --git a/drivers/input/misc/vibrator/drv2605l.c b/drivers/input/misc/vibrator/drv2605l.c
new file mode 100644
index 000000000000..fe46f3e8976e
--- /dev/null
+++ b/drivers/input/misc/vibrator/drv2605l.c
@@ -0,0 +1,1125 @@
+/*
+ ** =============================================================================
+ ** 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:
+ ** drv2605l.c
+ **
+ ** Description:
+ ** DRV2605L 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/firmware.h>
+#include "drv2605l.h"
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#define SKIP_AUTOCAL 0
+
+static struct reg_default drv260x_reg_defs[] = {
+ { 0x01, 0x05 },
+ { 0x02, 0x00 },
+ { 0x04, 0x00 },
+ { 0x1a, 0xb6 },
+ { 0x1b, 0x80 },
+ { 0x1c, 0x30 },
+ { 0x1d, 0x42 },
+ { 0x1e, 0x30 },
+ { 0x20, 0x33 },
+ { 0x21, 0x00 },
+ { 0x22, 0x00 },
+};
+
+static struct drv2605l_data *drv2605l_data = NULL;
+
+static unsigned int drv2605l_reg_read(struct drv2605l_data *haptics, unsigned int reg)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(haptics->regmap, reg, &val);
+
+ if (ret < 0)
+ return ret;
+ else
+ return val;
+}
+
+static int drv2605l_reg_write(struct drv2605l_data *haptics, unsigned char reg, unsigned int val)
+{
+ return regmap_write(haptics->regmap, reg, val);
+}
+
+static int drv2605l_bulk_read(struct drv2605l_data *haptics, unsigned char reg, unsigned int count, u8 *buf)
+{
+ return regmap_bulk_read(haptics->regmap, reg, buf, count);
+}
+
+static int drv2605l_bulk_write(struct drv2605l_data *haptics, unsigned char reg, unsigned int count, const u8 *buf)
+{
+ return regmap_bulk_write(haptics->regmap, reg, buf, count);
+}
+
+static int drv2605l_set_bits(struct drv2605l_data *haptics, unsigned char reg, unsigned char mask, unsigned int val)
+{
+ return regmap_update_bits(haptics->regmap, reg, mask, val);
+}
+
+static int drv2605l_set_go_bit(struct drv2605l_data *haptics, unsigned int val)
+{
+ return drv2605l_reg_write(haptics, GO_REG, (val&0x01));
+}
+
+#if SKIP_AUTOCAL == 0
+static void drv2605l_poll_go_bit(struct drv2605l_data *haptics)
+{
+ while (drv2605l_reg_read(haptics, GO_REG) == GO)
+ schedule_timeout_interruptible(msecs_to_jiffies(GO_BIT_POLL_INTERVAL));
+}
+#endif
+
+static int drv2605l_set_rtp_val(struct drv2605l_data *haptics, unsigned int value)
+{
+ /* please be noted: in unsigned mode, maximum is 0xff, in signed mode, maximum is 0x7f */
+ return drv2605l_reg_write(haptics, REAL_TIME_PLAYBACK_REG, value);
+}
+
+static int drv2605l_set_waveform_sequence(struct drv2605l_data *haptics, unsigned char* seq, unsigned int size)
+{
+ return drv2605l_bulk_write(haptics, WAVEFORM_SEQUENCER_REG, (size>WAVEFORM_SEQUENCER_MAX)?WAVEFORM_SEQUENCER_MAX:size, seq);
+}
+
+static void drv2605l_change_mode(struct drv2605l_data *haptics, char work_mode, char dev_mode)
+{
+ /* please be noted : LRA open loop cannot be used with analog input mode */
+// printk(KERN_DEBUG"%s:%x,%x\n", __FUNCTION__, (int)work_mode, (int)dev_mode);
+ if (dev_mode == DEV_IDLE) {
+ haptics->dev_mode = dev_mode;
+ haptics->work_mode = work_mode;
+ } else if (dev_mode == DEV_STANDBY) {
+ if (haptics->dev_mode != DEV_STANDBY) {
+ haptics->dev_mode = DEV_STANDBY;
+ drv2605l_reg_write(haptics, MODE_REG, MODE_STANDBY);
+ schedule_timeout_interruptible(msecs_to_jiffies(WAKE_STANDBY_DELAY));
+ }
+ haptics->work_mode = WORK_IDLE;
+ } else if (dev_mode == DEV_READY) {
+ if ((work_mode != haptics->work_mode)
+ ||(dev_mode != haptics->dev_mode)) {
+ haptics->work_mode = work_mode;
+ haptics->dev_mode = dev_mode;
+ if((haptics->work_mode == WORK_VIBRATOR)
+ ||(haptics->work_mode == WORK_PATTERN_RTP_ON)
+ ||(haptics->work_mode == WORK_SEQ_RTP_ON)
+ ||(haptics->work_mode == WORK_RTP)){
+ // printk(KERN_DEBUG"%s:MODE_REAL_TIME_PLAYBACK\n", __FUNCTION__);
+ drv2605l_reg_write(haptics, MODE_REG, MODE_REAL_TIME_PLAYBACK);
+ } else if (haptics->work_mode == WORK_CALIBRATION) {
+ drv2605l_reg_write(haptics, MODE_REG, AUTO_CALIBRATION);
+ } else {
+ drv2605l_reg_write(haptics, MODE_REG, MODE_INTERNAL_TRIGGER);
+ schedule_timeout_interruptible(msecs_to_jiffies(STANDBY_WAKE_DELAY));
+ }
+ }
+ }
+}
+
+static void play_effect(struct drv2605l_data *haptics)
+{
+ switch_set_state(&haptics->sw_dev, SW_STATE_SEQUENCE_PLAYBACK);
+ drv2605l_change_mode(haptics, WORK_SEQ_PLAYBACK, DEV_READY);
+ drv2605l_set_waveform_sequence(haptics, haptics->sequence, WAVEFORM_SEQUENCER_MAX);
+ haptics->is_playing = YES;
+ drv2605l_set_go_bit(haptics, GO);
+
+ while ((drv2605l_reg_read(haptics, GO_REG) == GO) && (haptics->should_stop == NO)) {
+ schedule_timeout_interruptible(msecs_to_jiffies(GO_BIT_POLL_INTERVAL));
+ }
+
+ if (haptics->should_stop == YES) {
+ drv2605l_set_go_bit(haptics, STOP);
+ }
+
+ drv2605l_change_mode(haptics, WORK_IDLE, DEV_STANDBY);
+ switch_set_state(&haptics->sw_dev, SW_STATE_IDLE);
+ haptics->is_playing = NO;
+ wake_unlock(&haptics->wk_lock);
+}
+
+static void play_pattern_rtp(struct drv2605l_data *haptics)
+{
+ if (haptics->work_mode == WORK_PATTERN_RTP_ON) {
+ drv2605l_change_mode(haptics, WORK_PATTERN_RTP_OFF, DEV_READY);
+ if (haptics->repeat_times == 0) {
+ drv2605l_change_mode(haptics, WORK_IDLE, DEV_STANDBY);
+ haptics->is_playing = NO;
+ switch_set_state(&haptics->sw_dev, SW_STATE_IDLE);
+ wake_unlock(&haptics->wk_lock);
+ } else {
+ hrtimer_start(&haptics->timer, ns_to_ktime((u64)haptics->silience_time * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ }
+ } else if (haptics->work_mode == WORK_PATTERN_RTP_OFF) {
+ haptics->repeat_times--;
+ drv2605l_change_mode(haptics, WORK_PATTERN_RTP_ON, DEV_READY);
+ hrtimer_start(&haptics->timer, ns_to_ktime((u64)haptics->vibration_time * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ }
+}
+
+static void play_seq_rtp(struct drv2605l_data *haptics)
+{
+ if (haptics->rtp_seq.rtp_index < haptics->rtp_seq.rtp_counts) {
+ int rtp_time = haptics->rtp_seq.rtp_data[haptics->rtp_seq.rtp_index] >> 8;
+ int rtp_val = haptics->rtp_seq.rtp_data[haptics->rtp_seq.rtp_index] & 0x00ff ;
+
+ haptics->is_playing = YES;
+ haptics->rtp_seq.rtp_index++;
+ drv2605l_change_mode(haptics, WORK_SEQ_RTP_ON, DEV_READY);
+ drv2605l_set_rtp_val(haptics, rtp_val);
+
+ hrtimer_start(&haptics->timer, ns_to_ktime((u64)rtp_time * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ } else {
+ drv2605l_change_mode(haptics, WORK_IDLE, DEV_STANDBY);
+ haptics->is_playing = NO;
+ switch_set_state(&haptics->sw_dev, SW_STATE_IDLE);
+ wake_unlock(&haptics->wk_lock);
+ }
+}
+
+static void vibrator_off(struct drv2605l_data *haptics)
+{
+ if (haptics->is_playing) {
+ haptics->is_playing = NO;
+ drv2605l_set_go_bit(haptics, STOP);
+ drv2605l_change_mode(haptics, WORK_IDLE, DEV_STANDBY);
+ switch_set_state(&haptics->sw_dev, SW_STATE_IDLE);
+ wake_unlock(&haptics->wk_lock);
+ }
+}
+
+static void drv2605l_stop(struct drv2605l_data *haptics)
+{
+ if (haptics->is_playing) {
+ if ((haptics->work_mode == WORK_VIBRATOR)
+ ||(haptics->work_mode == WORK_PATTERN_RTP_ON)
+ ||(haptics->work_mode == WORK_PATTERN_RTP_OFF)
+ ||(haptics->work_mode == WORK_SEQ_RTP_ON)
+ ||(haptics->work_mode == WORK_SEQ_RTP_OFF)
+ ||(haptics->work_mode == WORK_RTP)) {
+ vibrator_off(haptics);
+ } else if (haptics->work_mode == WORK_SEQ_PLAYBACK) {
+ //do nothing
+ } else {
+ printk("%s, err mode=%d \n", __FUNCTION__, haptics->work_mode);
+ }
+ }
+}
+
+static int vibrator_get_time(struct timed_output_dev *dev)
+{
+ struct drv2605l_data *haptics = container_of(dev, struct drv2605l_data, to_dev);
+
+ if (hrtimer_active(&haptics->timer)) {
+ ktime_t r = hrtimer_get_remaining(&haptics->timer);
+ return ktime_to_ms(r);
+ }
+
+ return 0;
+}
+
+static void vibrator_enable( struct timed_output_dev *dev, int value)
+{
+ struct drv2605l_data *haptics = container_of(dev, struct drv2605l_data, to_dev);
+
+ //printk(KERN_DEBUG"%s:Enter\n", __FUNCTION__);
+ haptics->should_stop = YES;
+ hrtimer_cancel(&haptics->timer);
+ cancel_work_sync(&haptics->vibrator_work);
+
+ mutex_lock(&haptics->lock);
+
+ drv2605l_stop(haptics);
+// printk(KERN_DEBUG"%s:value:%d\n", __FUNCTION__, value);
+ if (value > 0) {
+ wake_lock(&haptics->wk_lock);
+
+ drv2605l_change_mode(haptics, WORK_VIBRATOR, DEV_READY);
+ haptics->is_playing = YES;
+ switch_set_state(&haptics->sw_dev, SW_STATE_RTP_PLAYBACK);
+ gpiod_set_value(haptics->platform_data.enable_gpio, 1);
+ value = (value>MAX_TIMEOUT)?MAX_TIMEOUT:value;
+ hrtimer_start(&haptics->timer, ns_to_ktime((u64)value * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ }
+
+ mutex_unlock(&haptics->lock);
+// printk(KERN_DEBUG"%s:Exit\n", __FUNCTION__);
+}
+
+static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
+{
+ struct drv2605l_data *haptics = container_of(timer, struct drv2605l_data, timer);
+
+ //printk(KERN_DEBUG"%s:Enter\n", __FUNCTION__);
+ schedule_work(&haptics->vibrator_work);
+ //printk(KERN_DEBUG"%s:Exit\n", __FUNCTION__);
+
+ return HRTIMER_NORESTART;
+}
+
+static void vibrator_work_routine(struct work_struct *work)
+{
+ struct drv2605l_data *haptics = container_of(work, struct drv2605l_data, vibrator_work);
+
+// printk(KERN_DEBUG"%s:Enter\n", __FUNCTION__);
+
+// printk(KERN_DEBUG"%s:work_mode:%x\n", __FUNCTION__, (int)(haptics->work_mode));
+ mutex_lock(&haptics->lock);
+ if ((haptics->work_mode == WORK_VIBRATOR)
+ ||(haptics->work_mode == WORK_RTP)) {
+ vibrator_off(haptics);
+ } else if (haptics->work_mode == WORK_SEQ_PLAYBACK) {
+ play_effect(haptics);
+ } else if ((haptics->work_mode == WORK_PATTERN_RTP_ON)
+ ||(haptics->work_mode == WORK_PATTERN_RTP_OFF)) {
+ play_pattern_rtp(haptics);
+ } else if ((haptics->work_mode == WORK_SEQ_RTP_ON)
+ ||(haptics->work_mode == WORK_SEQ_RTP_OFF)) {
+ play_seq_rtp(haptics);
+ }
+ mutex_unlock(&haptics->lock);
+
+ //printk(KERN_DEBUG"%s:Exit\n", __FUNCTION__);
+}
+
+static int fw_chksum(const struct firmware *fw){
+ int sum = 0;
+ int i=0;
+ int size = fw->size;
+ const unsigned char *pbuf = fw->data;
+
+ for (i=0; i< size; i++) {
+ if((i>11) && (i<16)) {
+
+ } else {
+ sum += pbuf[i];
+ }
+ }
+
+ return sum;
+}
+
+/* drv2605l_firmware_load: This function is called by the
+ * request_firmware_nowait function as soon
+ * as the firmware has been loaded from the file.
+ * The firmware structure contains the data and$
+ * the size of the firmware loaded.
+ * @fw: pointer to firmware file to be dowloaded
+ * @context: pointer variable to drv2605l_data
+ *
+ *
+ */
+static void drv2605l_firmware_load(const struct firmware *fw, void *context)
+{
+ struct drv2605l_data *haptics = context;
+ int size = 0, fwsize = 0, i=0;
+ const unsigned char *pbuf = NULL;
+
+ if (fw != NULL) {
+ pbuf = fw->data;
+ size = fw->size;
+
+ memcpy(&(haptics->fw_header), pbuf, sizeof(struct drv2605l_fw_header));
+ if ((haptics->fw_header.fw_magic != DRV2605L_MAGIC)
+ ||(haptics->fw_header.fw_size != size)
+ ||(haptics->fw_header.fw_chksum != fw_chksum(fw))) {
+ printk("%s, ERROR!! firmware not right:Magic=0x%x,Size=%d,chksum=0x%x\n",
+ __FUNCTION__, haptics->fw_header.fw_magic,
+ haptics->fw_header.fw_size, haptics->fw_header.fw_chksum);
+ } else {
+ printk("%s, firmware good\n", __FUNCTION__);
+
+ drv2605l_change_mode(haptics, WORK_IDLE, DEV_READY);
+
+ pbuf += sizeof(struct drv2605l_fw_header);
+
+ drv2605l_reg_write(haptics, RAM_ADDR_UPPER_BYTE_REG, 0);
+ drv2605l_reg_write(haptics, RAM_ADDR_LOWER_BYTE_REG, 0);
+
+ fwsize = size - sizeof(struct drv2605l_fw_header);
+ for (i = 0; i < fwsize; i++) {
+ drv2605l_reg_write(haptics, RAM_DATA_REG, pbuf[i]);
+ }
+
+ drv2605l_change_mode(haptics, WORK_IDLE, DEV_STANDBY);
+ }
+ } else {
+ printk("%s, ERROR!! firmware not found\n", __FUNCTION__);
+ }
+}
+
+static int dev2605l_open (struct inode * i_node, struct file * filp)
+{
+ if(drv2605l_data == NULL) {
+ return -ENODEV;
+ }
+
+ filp->private_data = drv2605l_data;
+ return 0;
+}
+
+static ssize_t dev2605l_read(struct file* filp, char* buff, size_t length, loff_t* offset)
+{
+ struct drv2605l_data *haptics = (struct drv2605l_data *)filp->private_data;
+ int ret = 0;
+
+ if (haptics->r_len > 0) {
+ ret = copy_to_user(buff, haptics->r_buff, haptics->r_len);
+ if (ret != 0) {
+ printk("%s, copy_to_user err=%d \n", __FUNCTION__, ret);
+ } else {
+ ret = haptics->r_len;
+ }
+ haptics->r_len = 0;
+ } else {
+ printk("%s, nothing to read\n", __FUNCTION__);
+ }
+
+ return ret;
+}
+
+static bool is_for_debug(int cmd){
+ return ((cmd == HAPTIC_CMDID_REG_WRITE)
+ ||(cmd == HAPTIC_CMDID_REG_READ)
+ ||(cmd == HAPTIC_CMDID_REG_SETBIT));
+}
+
+static ssize_t dev2605l_write(struct file* filp, const char* buff, size_t len, loff_t* off)
+{
+ struct drv2605l_data *haptics = (struct drv2605l_data *)filp->private_data;
+
+ if (is_for_debug(buff[0])) {
+ //do nothing
+ } else {
+ haptics->should_stop = YES;
+ hrtimer_cancel(&haptics->timer);
+ cancel_work_sync(&haptics->vibrator_work);
+ }
+
+ mutex_lock(&haptics->lock);
+
+ if (is_for_debug(buff[0])) {
+ //do nothing
+ } else {
+ drv2605l_stop(haptics);
+ }
+
+ switch (buff[0]) {
+ case HAPTIC_CMDID_PLAY_SINGLE_EFFECT:
+ case HAPTIC_CMDID_PLAY_EFFECT_SEQUENCE: {
+ memset(&haptics->sequence, 0, WAVEFORM_SEQUENCER_MAX);
+ if (!copy_from_user(&haptics->sequence, &buff[1], len - 1))
+ {
+ wake_lock(&haptics->wk_lock);
+
+ haptics->should_stop = NO;
+ drv2605l_change_mode(haptics, WORK_SEQ_PLAYBACK, DEV_IDLE);
+ schedule_work(&haptics->vibrator_work);
+ }
+ break;
+ }
+ case HAPTIC_CMDID_PLAY_TIMED_EFFECT: {
+ unsigned int value = 0;
+ value = buff[2];
+ value <<= 8;
+ value |= buff[1];
+
+ if (value > 0) {
+ wake_lock(&haptics->wk_lock);
+ switch_set_state(&haptics->sw_dev, SW_STATE_RTP_PLAYBACK);
+ haptics->is_playing = YES;
+ value = (value > MAX_TIMEOUT)?MAX_TIMEOUT:value;
+ drv2605l_change_mode(haptics, WORK_RTP, DEV_READY);
+
+ hrtimer_start(&haptics->timer, ns_to_ktime((u64)value * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ }
+ break;
+ }
+ case HAPTIC_CMDID_PATTERN_RTP: {
+ unsigned char strength = 0;
+
+ haptics->vibration_time = (int)((((int)buff[2])<<8) | (int)buff[1]);
+ haptics->silience_time = (int)((((int)buff[4])<<8) | (int)buff[3]);
+ strength = buff[5];
+ haptics->repeat_times = buff[6];
+
+ if (haptics->vibration_time > 0) {
+ wake_lock(&haptics->wk_lock);
+ switch_set_state(&haptics->sw_dev, SW_STATE_RTP_PLAYBACK);
+ haptics->is_playing = YES;
+ if (haptics->repeat_times > 0)
+ haptics->repeat_times--;
+ if (haptics->vibration_time > MAX_TIMEOUT)
+ haptics->vibration_time = MAX_TIMEOUT;
+ drv2605l_change_mode(haptics, WORK_PATTERN_RTP_ON, DEV_READY);
+ drv2605l_set_rtp_val(haptics, strength);
+
+ hrtimer_start(&haptics->timer, ns_to_ktime((u64)haptics->vibration_time * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ }
+ break;
+ }
+ case HAPTIC_CMDID_RTP_SEQUENCE: {
+ memset(&haptics->rtp_seq, 0, sizeof(struct rtp_seq));
+ if (((len-1)%2) == 0) {
+ haptics->rtp_seq.rtp_counts = (len-1)/2;
+ if ((haptics->rtp_seq.rtp_counts <= MAX_RTP_SEQ)
+ && (haptics->rtp_seq.rtp_counts>0)) {
+ if (copy_from_user(haptics->rtp_seq.rtp_data, &buff[1], haptics->rtp_seq.rtp_counts*2) != 0) {
+ printk("%s, rtp_seq copy seq err\n", __FUNCTION__);
+ break;
+ }
+
+ wake_lock(&haptics->wk_lock);
+ switch_set_state(&haptics->sw_dev, SW_STATE_RTP_PLAYBACK);
+ drv2605l_change_mode(haptics, WORK_SEQ_RTP_OFF, DEV_IDLE);
+ schedule_work(&haptics->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_UPDATE_FIRMWARE: {
+ struct firmware fw;
+ unsigned char *fw_buffer = (unsigned char *)kzalloc(len-1, GFP_KERNEL);
+ int result = -1;
+
+ if (fw_buffer != NULL) {
+ fw.size = len-1;
+
+ wake_lock(&haptics->wk_lock);
+ result = copy_from_user(fw_buffer, &buff[1], fw.size);
+ if (result == 0) {
+ printk("%s, fwsize=%d, f:%x, l:%x\n", __FUNCTION__, (int)fw.size, buff[1], buff[len-1]);
+ fw.data = (const unsigned char *)fw_buffer;
+ drv2605l_firmware_load(&fw, (void *)haptics);
+ }
+ wake_unlock(&haptics->wk_lock);
+
+ kfree(fw_buffer);
+ }
+ break;
+ }
+
+ case HAPTIC_CMDID_READ_FIRMWARE: {
+ int i;
+ if (len == 3) {
+ haptics->r_len = 1;
+ drv2605l_reg_write(haptics, RAM_ADDR_UPPER_BYTE_REG, buff[2]);
+ drv2605l_reg_write(haptics, RAM_ADDR_LOWER_BYTE_REG, buff[1]);
+ haptics->r_buff[0] = drv2605l_reg_read(haptics, RAM_DATA_REG);
+ } else if (len == 4) {
+ drv2605l_reg_write(haptics, RAM_ADDR_UPPER_BYTE_REG, buff[2]);
+ drv2605l_reg_write(haptics, RAM_ADDR_LOWER_BYTE_REG, buff[1]);
+ haptics->r_len = (buff[3]>MAX_READ_BYTES)?MAX_READ_BYTES:buff[3];
+ for(i=0; i < haptics->r_len; i++){
+ haptics->r_buff[i] = drv2605l_reg_read(haptics, RAM_DATA_REG);
+ }
+ } else {
+ printk("%s, read fw len error\n", __FUNCTION__);
+ }
+ break;
+ }
+ case HAPTIC_CMDID_REG_READ: {
+ if (len == 2) {
+ haptics->r_len = 1;
+ haptics->r_buff[0] = drv2605l_reg_read(haptics, buff[1]);
+ } else if (len == 3) {
+ haptics->r_len = (buff[2]>MAX_READ_BYTES)?MAX_READ_BYTES:buff[2];
+ drv2605l_bulk_read(haptics, buff[1], haptics->r_len, haptics->r_buff);
+ } else {
+ printk("%s, reg_read len error\n", __FUNCTION__);
+ }
+ break;
+ }
+ case HAPTIC_CMDID_REG_WRITE: {
+ if ((len-1) == 2) {
+ drv2605l_reg_write(haptics, 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 {
+ drv2605l_bulk_write(haptics, 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; ) {
+ drv2605l_set_bits(haptics, buff[i], buff[i+1], buff[i+2]);
+ i += 3;
+ }
+ break;
+ }
+ default:
+ printk("%s, unknown HAPTIC cmd\n", __FUNCTION__);
+ break;
+ }
+
+ mutex_unlock(&haptics->lock);
+
+ return len;
+}
+
+
+static struct file_operations fops =
+{
+ .open = dev2605l_open,
+ .read = dev2605l_read,
+ .write = dev2605l_write,
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+void drv2605l_early_suspend(struct early_suspend *h){
+ struct drv2605l_data *haptics = container_of(h, struct drv2605l_data, early_suspend);
+
+ haptics->should_stop = YES;
+ hrtimer_cancel(&haptics->timer);
+ cancel_work_sync(&haptics->vibrator_work);
+
+ mutex_lock(&haptics->lock);
+
+ drv2605l_stop(haptics);
+
+ mutex_unlock(&haptics->lock);
+ return ;
+}
+
+void drv2605l_late_resume(struct early_suspend *h) {
+ struct drv2605l_data *haptics = container_of(h, struct drv2605l_data, early_suspend);
+
+ mutex_lock(&haptics->lock);
+ mutex_unlock(&haptics->lock);
+ return ;
+}
+#endif
+
+static int drv2605l_init(struct drv2605l_data *haptics)
+{
+ int reval = -ENOMEM;
+
+ haptics->version = MKDEV(0,0);
+ reval = alloc_chrdev_region(&haptics->version, 0, 1, "drv2605l");
+ if (reval < 0) {
+ printk(KERN_ALERT"drv2605: error getting major number %d\n", reval);
+ goto fail0;
+ }
+
+ haptics->class = class_create(THIS_MODULE, "drv260x");
+ if (!haptics->class) {
+ printk(KERN_ALERT"drv2605: error creating class\n");
+ goto fail1;
+ }
+
+ haptics->device = device_create(haptics->class, NULL, haptics->version, NULL, "drv2605l");
+ if (!haptics->device) {
+ printk(KERN_ALERT"drv2605: error creating device 2605\n");
+ goto fail2;
+ }
+
+ cdev_init(&haptics->cdev, &fops);
+ haptics->cdev.owner = THIS_MODULE;
+ haptics->cdev.ops = &fops;
+ reval = cdev_add(&haptics->cdev, haptics->version, 1);
+ if (reval) {
+ printk(KERN_ALERT"drv2605: fail to add cdev\n");
+ goto fail3;
+ }
+
+ haptics->sw_dev.name = "haptics";
+ reval = switch_dev_register(&haptics->sw_dev);
+ if (reval < 0) {
+ printk(KERN_ALERT"drv2605: fail to register switch\n");
+ goto fail4;
+ }
+
+ haptics->to_dev.name = "vibrator";
+ haptics->to_dev.get_time = vibrator_get_time;
+ haptics->to_dev.enable = vibrator_enable;
+
+ if (timed_output_dev_register(&(haptics->to_dev)) < 0) {
+ printk(KERN_ALERT"drv2605: fail to create timed output dev\n");
+ goto fail3;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ haptics->early_suspend.suspend = drv2605l_early_suspend;
+ haptics->early_suspend.resume = drv2605l_late_resume;
+ haptics->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN - 1;
+ register_early_suspend(&haptics->early_suspend);
+#endif
+
+ hrtimer_init(&haptics->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ haptics->timer.function = vibrator_timer_func;
+ INIT_WORK(&haptics->vibrator_work, vibrator_work_routine);
+
+ wake_lock_init(&haptics->wk_lock, WAKE_LOCK_SUSPEND, "vibrator");
+ mutex_init(&haptics->lock);
+
+ return 0;
+
+fail4:
+ switch_dev_unregister(&haptics->sw_dev);
+fail3:
+ device_destroy(haptics->class, haptics->version);
+fail2:
+ class_destroy(haptics->class);
+fail1:
+ unregister_chrdev_region(haptics->version, 1);
+fail0:
+ return reval;
+}
+
+static void dev_init_platform_data(struct drv2605l_data *haptics)
+{
+ struct drv2605l_platform_data *pdata = &haptics->platform_data;
+ struct actuator_data actuator = pdata->actuator;
+ unsigned char ctrlx_temp = 0;
+ //otp memory saves data from 0x16 to 0x1a
+ if (haptics->otp == 0) {
+ if (actuator.rated_voltage != 0) {
+ drv2605l_reg_write(haptics, RATED_VOLTAGE_REG, actuator.rated_voltage);
+ } else {
+ printk("%s, ERROR Rated ZERO\n", __FUNCTION__);
+ }
+
+ if(actuator.overdrive_voltage != 0) {
+ drv2605l_reg_write(haptics, OVERDRIVE_CLAMP_VOLTAGE_REG, actuator.overdrive_voltage);
+ } else {
+ printk("%s, ERROR OverDriveVol ZERO\n", __FUNCTION__);
+ }
+
+ drv2605l_set_bits(haptics,
+ FEEDBACK_CONTROL_REG,
+ FEEDBACK_CONTROL_DEVICE_TYPE_MASK
+ |FEEDBACK_CONTROL_FB_BRAKE_MASK
+ |FEEDBACK_CONTROL_LOOP_GAIN_MASK,
+ (((actuator.device_type == LRA)?FEEDBACK_CONTROL_MODE_LRA:FEEDBACK_CONTROL_MODE_ERM)
+ |FB_BRAKE_FACTOR
+ |LOOP_GAIN)
+ );
+ } else {
+ printk("%s, otp programmed\n", __FUNCTION__);
+ }
+
+ if (actuator.device_type == LRA) {
+ unsigned char drive_time = 5*(1000 - actuator.lra_freq)/actuator.lra_freq;
+ drv2605l_set_bits(haptics,
+ CTRL1_REG,
+ CTRL1_REG_DRIVE_TIME_MASK,
+ drive_time);
+ printk("%s, LRA = %d, drive_time=0x%x\n", __FUNCTION__, actuator.lra_freq, drive_time);
+ }
+
+ if (pdata->loop_mode == OPEN_LOOP) {
+ ctrlx_temp = BIDIR_INPUT_BIDIRECTIONAL;
+ } else {
+ if(pdata->input_mode == UNIDIRECTIONAL) {
+ ctrlx_temp = BIDIR_INPUT_UNIDIRECTIONAL;
+ } else {
+ ctrlx_temp = BIDIR_INPUT_BIDIRECTIONAL;
+ }
+ }
+
+ drv2605l_set_bits(haptics,
+ CTRL2_REG,
+ CTRL2_REG_BIDIR_INPUT_MASK|BLANKING_TIME_MASK|IDISS_TIME_MASK,
+ ctrlx_temp|BLANKING_TIME|IDISS_TIME);
+
+ if ((pdata->loop_mode == CLOSE_LOOP)&&(actuator.device_type == LRA)) {
+ drv2605l_set_bits(haptics,
+ CTRL2_REG,
+ AUTO_RES_SAMPLE_TIME_MASK,
+ AUTO_RES_SAMPLE_TIME_300us);
+ }
+
+ if ((pdata->loop_mode == OPEN_LOOP)&&(actuator.device_type == LRA)) {
+ ctrlx_temp = LRA_OpenLoop_Enabled;
+ } else if ((pdata->loop_mode == OPEN_LOOP)&&(actuator.device_type == ERM)) {
+ ctrlx_temp = ERM_OpenLoop_Enabled;
+ } else {
+ ctrlx_temp = ERM_OpenLoop_Disable|LRA_OpenLoop_Disable;
+ }
+
+ if ((pdata->loop_mode == CLOSE_LOOP)
+ && (pdata->input_mode == UNIDIRECTIONAL)) {
+ ctrlx_temp |= RTP_FORMAT_UNSIGNED;
+ drv2605l_reg_write(haptics, REAL_TIME_PLAYBACK_REG, 0xff);
+ } else {
+ if(pdata->rtp_format == SIGNED) {
+ ctrlx_temp |= RTP_FORMAT_SIGNED;
+ drv2605l_reg_write(haptics, REAL_TIME_PLAYBACK_REG, 0x7f);
+ } else {
+ ctrlx_temp |= RTP_FORMAT_UNSIGNED;
+ drv2605l_reg_write(haptics, REAL_TIME_PLAYBACK_REG, 0xff);
+ }
+ }
+
+ drv2605l_set_bits(haptics,
+ CTRL3_REG,
+ CTRL3_REG_LOOP_MASK|CTRL3_REG_FORMAT_MASK,
+ ctrlx_temp);
+
+ drv2605l_set_bits(haptics,
+ CTRL4_REG,
+ CTRL4_REG_CAL_TIME_MASK|CTRL4_REG_ZC_DET_MASK,
+ AUTO_CAL_TIME|ZC_DET_TIME
+ );
+
+ drv2605l_set_bits(haptics,
+ CTRL5_REG,
+ BLANK_IDISS_MSB_MASK,
+ BLANK_IDISS_MSB_CLEAR
+ );
+
+ if (actuator.device_type == LRA) {
+ /* please refer to the equations in DRV2605L data sheet */
+ unsigned int ctrlx_temp = 9846 * actuator.lra_freq;
+ unsigned int reg_20 = (unsigned int)(100000000 / ctrlx_temp);
+ drv2605l_reg_write(haptics, LRA_OPENLOOP_PERIOD_REG, reg_20);
+ }
+}
+
+#if SKIP_AUTOCAL == 0
+static int dev_auto_calibrate(struct drv2605l_data *haptics)
+{
+ int err = 0, status=0;
+
+ drv2605l_change_mode(haptics, WORK_CALIBRATION, DEV_READY);
+ drv2605l_set_go_bit(haptics, GO);
+
+ /* Wait until the procedure is done */
+ drv2605l_poll_go_bit(haptics);
+ /* Read status */
+ status = drv2605l_reg_read(haptics, STATUS_REG);
+
+ printk("%s, calibration status =0x%x\n", __FUNCTION__, status);
+
+ /* Read calibration results */
+ drv2605l_reg_read(haptics, AUTO_CALI_RESULT_REG);
+ drv2605l_reg_read(haptics, AUTO_CALI_BACK_EMF_RESULT_REG);
+ drv2605l_reg_read(haptics, FEEDBACK_CONTROL_REG);
+
+ return err;
+}
+#endif
+
+#define DRV260X_MAX_REG 0x23
+static struct regmap_config drv2605l_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = DRV260X_MAX_REG,
+ .reg_defaults = drv260x_reg_defs,
+ .num_reg_defaults = ARRAY_SIZE(drv260x_reg_defs),
+ .cache_type = REGCACHE_NONE,
+};
+/*
+static void HapticsFirmwareLoad(const struct firmware *fw, void *context)
+{
+ drv2605l_firmware_load(fw, context);
+ release_firmware(fw);
+}
+*/
+
+/**
+ * Rated and Overdriver Voltages:
+ * Calculated using the formula r = v * 255 / 5.6
+ * where r is what will be written to the register
+ * and v is the rated or overdriver voltage of the actuator
+ **/
+static int drv2605l_calculate_voltage(unsigned int voltage)
+{
+ return (voltage * 255 / 5600);
+}
+
+#ifdef CONFIG_OF
+static int drv2605l_parse_dt(struct device *dev,
+ struct drv2605l_data *haptics)
+{
+ struct device_node *np = dev->of_node;
+ struct drv2605l_platform_data *pdata = &haptics->platform_data;
+ unsigned int voltage;
+ int error;
+
+ error = of_property_read_u32(np, "input-mode", &pdata->input_mode);
+ if (error) {
+ dev_err(dev, "%s: No entry for input mode\n", __func__);
+ return error;
+ }
+
+ error = of_property_read_u32(np, "loop-mode", &pdata->loop_mode);
+ if (error) {
+ dev_err(dev, "%s: No entry for loop mode\n", __func__);
+ return error;
+ }
+
+ error = of_property_read_u32(np, "rtp-format", &pdata->rtp_format);
+ if (error) {
+ dev_err(dev, "%s: No entry for mode\n", __func__);
+ return error;
+ }
+
+ error = of_property_read_u32(np, "vib-rated-mv", &voltage);
+ if (!error)
+ pdata->actuator.rated_voltage = drv2605l_calculate_voltage(voltage);
+
+ error = of_property_read_u32(np, "vib-overdrive-mv", &voltage);
+ if (!error)
+ pdata->actuator.overdrive_voltage= drv2605l_calculate_voltage(voltage);
+
+ error = of_property_read_u32(np, "frequency", &pdata->actuator.lra_freq);
+ if (error) {
+ dev_err(dev, "%s: No entry for frequency\n", __func__);
+ return error;
+ }
+
+ error = of_property_read_u32(np, "actuator-type", &pdata->actuator.device_type);
+ if (error) {
+ dev_err(dev, "%s: No entry for actuator type\n", __func__);
+ return error;
+ }
+
+ return 0;
+}
+#else
+static inline int drv2605l_parse_dt(struct device *dev,
+ struct drv2605l_data *haptics)
+{
+ dev_err(dev, "no platform data defined\n");
+
+ return -EINVAL;
+}
+#endif
+
+static int drv2605l_probe(struct i2c_client* client, const struct i2c_device_id* id)
+{
+ struct drv2605l_data *haptics;
+ struct drv2605l_platform_data *pdata = dev_get_platdata(&client->dev);
+ unsigned int reg_val = 0;
+ int err = 0;
+
+ printk(KERN_DEBUG"%s:Enter\n", __FUNCTION__);
+
+ haptics = devm_kzalloc(&client->dev, sizeof(struct drv2605l_data), GFP_KERNEL);
+ if (haptics == NULL) {
+ printk(KERN_ERR"%s:no memory\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ if (pdata) {
+ memcpy(&haptics->platform_data, pdata, sizeof(struct drv2605l_platform_data));
+ }
+ else if (client->dev.of_node) {
+ err = drv2605l_parse_dt(&client->dev, haptics);
+ if (err)
+ return err;
+ }
+
+ haptics->platform_data.enable_gpio = devm_gpiod_get(&client->dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(haptics->platform_data.enable_gpio)) {
+ err = PTR_ERR(haptics->platform_data.enable_gpio);
+ if (err != -ENOENT && err != -ENOSYS)
+ return err;
+ haptics->platform_data.enable_gpio = NULL;
+ } else {
+ gpiod_direction_output(haptics->platform_data.enable_gpio, 1);
+ udelay(250);
+ }
+
+ haptics->regmap = devm_regmap_init_i2c(client, &drv2605l_i2c_regmap);
+ if (IS_ERR(haptics->regmap)) {
+ err = PTR_ERR(haptics->regmap);
+ printk(KERN_ERR"%s:Failed to allocate register map: %d\n",__FUNCTION__,err);
+ return err;
+ }
+
+ i2c_set_clientdata(client,haptics);
+
+ reg_val = drv2605l_reg_read(haptics, STATUS_REG);
+ printk("%s, status=%d\n", __FUNCTION__, reg_val);
+
+ /* Read device ID */
+ haptics->device_id = (reg_val & DEV_ID_MASK);
+ switch (haptics->device_id) {
+ case DRV2605_VER_1DOT1:
+ printk("drv2604 driver found: drv2605 v1.1.\n");
+ break;
+ case DRV2605_VER_1DOT0:
+ printk("drv2604 driver found: drv2605 v1.0.\n");
+ break;
+ case DRV2604:
+ printk(KERN_ALERT"drv2604 driver found: drv2604.\n");
+ break;
+ case DRV2604L:
+ printk(KERN_ALERT"drv2604 driver found: drv2604L.\n");
+ break;
+ case DRV2605L:
+ printk(KERN_ALERT"drv2604 driver found: drv2605L.\n");
+ break;
+ default:
+ printk(KERN_ERR"drv2604 driver found: unknown.\n");
+ break;
+ }
+/*
+ if(haptics->device_id != DRV2604L){
+ printk("%s, status(0x%x),device_id(%d) fail\n",
+ __FUNCTION__, status, haptics->device_id);
+ goto exit_gpio_request_failed;
+ }else{
+ err = request_firmware_nowait(THIS_MODULE,
+ FW_ACTION_HOTPLUG,
+ "drv2604.bin",
+ &(client->dev),
+ GFP_KERNEL,
+ haptics,
+ HapticsFirmwareLoad);
+ }
+*/
+ drv2605l_change_mode(haptics, WORK_IDLE, DEV_READY);
+ schedule_timeout_interruptible(msecs_to_jiffies(STANDBY_WAKE_DELAY));
+
+ haptics->otp = drv2605l_reg_read(haptics, CTRL4_REG) & CTRL4_REG_OTP_MASK;
+
+ dev_init_platform_data(haptics);
+
+#if SKIP_AUTOCAL == 0
+ if (haptics->otp == 0) {
+ err = dev_auto_calibrate(haptics);
+ if (err < 0) {
+ printk("%s, ERROR, calibration fail\n", __FUNCTION__);
+ }
+ }
+#endif
+
+ /* Put hardware in standby */
+ drv2605l_change_mode(haptics, WORK_IDLE, DEV_STANDBY);
+
+ drv2605l_init(haptics);
+
+ drv2605l_data = haptics;
+ printk("drv2605 probe succeeded\n");
+
+ return 0;
+}
+
+static int drv2605l_remove(struct i2c_client* client)
+{
+ struct drv2605l_data *haptics = i2c_get_clientdata(client);
+
+ device_destroy(haptics->class, haptics->version);
+ class_destroy(haptics->class);
+ unregister_chrdev_region(haptics->version, 1);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&haptics->early_suspend);
+#endif
+ gpiod_set_value(haptics->platform_data.enable_gpio, 0);
+ printk(KERN_ALERT"drv2605 remove");
+
+ return 0;
+}
+
+static struct i2c_device_id drv2605l_id_table[] =
+{
+ { HAPTICS_DEVICE_NAME, 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, drv2605l_id_table);
+
+#ifdef CONFIG_OF
+static const struct of_device_id drv260x_of_match[] = {
+ { .compatible = "ti,drv2605l", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, drv260x_of_match);
+#endif
+
+static struct i2c_driver drv2605l_driver =
+{
+ .driver = {
+ .name = "drv2605l-haptics",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(drv260x_of_match),
+ },
+ .id_table = drv2605l_id_table,
+ .probe = drv2605l_probe,
+ .remove = drv2605l_remove,
+};
+
+static int __init drv2605x_init(void)
+{
+ return i2c_add_driver(&drv2605l_driver);
+}
+
+static void __exit drv2605x_exit(void)
+{
+ i2c_del_driver(&drv2605l_driver);
+}
+
+module_init(drv2605x_init);
+module_exit(drv2605x_exit);
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("Driver for "HAPTICS_DEVICE_NAME);
diff --git a/drivers/input/misc/vibrator/drv2605l.h b/drivers/input/misc/vibrator/drv2605l.h
new file mode 100644
index 000000000000..59627a4e5210
--- /dev/null
+++ b/drivers/input/misc/vibrator/drv2605l.h
@@ -0,0 +1,440 @@
+#ifndef __drv2605L_H__
+#define __drv2605L_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:
+** drv2605l.h
+**
+** Description:
+** Header file for drv2605l.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/cdev.h>
+
+#define HAPTICS_DEVICE_NAME "drv2605l"
+
+#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
+
+/*
+** 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 FEEDBACK_CONTROL_LOOP_GAIN_MASK 0x0C
+#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 FEEDBACK_CONTROL_FB_BRAKE_MASK 0x70
+#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 CTRL1_REG 0x1B
+#define CTRL1_REG_AC_COUPLE_MASK 0x20
+#define CTRL1_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
+
+/*
+** Control2
+*/
+#define CTRL2_REG 0x1C
+#define CTRL2_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_SAMPLE_TIME_MASK 0x30
+#define AUTO_RES_SAMPLE_TIME_150us (0 << 4)
+#define AUTO_RES_SAMPLE_TIME_200us (1 << 4)
+#define AUTO_RES_SAMPLE_TIME_250us (2 << 4) // default
+#define AUTO_RES_SAMPLE_TIME_300us (3 << 4)
+
+
+
+#define BIDIR_INPUT_MASK 0x80
+#define UNIDIRECT_INPUT (0 << 7)
+#define BRAKE_STABLIZER (1<<6)
+#define BIDIRECT_INPUT (1 << 7) // default
+
+/*
+** Control3
+*/
+#define CTRL3_REG 0x1D
+#define CTRL3_REG_LOOP_MASK 0x21
+#define CTRL3_REG_PWMANALOG_MASK 0x02
+#define CTRL3_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)
+
+/*
+** Control4
+*/
+#define CTRL4_REG 0x1E
+#define CTRL4_REG_OTP_MASK 0x04
+#define CTRL4_REG_CAL_TIME_MASK 0x30
+#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 CTRL4_REG_ZC_DET_MASK 0xC0
+#define ZC_DET_TIME_100us (0 << 4)
+#define ZC_DET_TIME_200us (1 << 4)
+#define ZC_DET_TIME_300us (2 << 4)
+#define ZC_DET_TIME_390us (3 << 4)
+
+/*
+** Control5
+*/
+#define CTRL5_REG 0x1F
+#define BLANK_IDISS_MSB_MASK 0x0f
+#define BLANK_IDISS_MSB_CLEAR 0
+
+/*
+** LRA Open Loop Period
+*/
+#define LRA_OPENLOOP_PERIOD_REG 0x20
+
+#define SILICON_REVISION_REG 0x3B
+#define SILICON_REVISION_MASK 0x07
+
+#define RAM_ADDR_UPPER_BYTE_REG 0xfd
+#define RAM_ADDR_LOWER_BYTE_REG 0xfe
+#define RAM_DATA_REG 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_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
+
+/* recommendations from data sheet */
+#define FB_BRAKE_FACTOR FB_BRAKE_FACTOR_3X
+#define LOOP_GAIN LOOP_RESPONSE_FAST
+#define AUTO_CAL_TIME AUTOCAL_TIME_1000MS
+#define AUTO_RES_SAMPLE_TIME AUTO_RES_SAMPLE_TIME_300us
+#define BLANKING_TIME BLANKING_TIME_SHORT
+#define IDISS_TIME IDISS_TIME_SHORT
+#define ZC_DET_TIME ZC_DET_TIME_100us
+
+enum actuator_type {
+ ERM,
+ LRA,
+};
+
+enum loop_mode {
+ OPEN_LOOP,
+ CLOSE_LOOP,
+};
+
+enum rtp_format {
+ SIGNED,
+ UNSIGNED,
+};
+
+enum input_mode {
+ UNIDIRECTIONAL,
+ BIDIRECTIONAL,
+};
+
+struct actuator_data {
+ enum actuator_type device_type;
+ unsigned int rated_voltage;
+ unsigned int overdrive_voltage;
+ unsigned int lra_freq;
+};
+
+struct drv2605l_platform_data {
+ struct gpio_desc *enable_gpio;
+ enum loop_mode loop_mode;
+ enum rtp_format rtp_format;
+ enum input_mode input_mode;
+ struct actuator_data actuator;
+};
+
+#define MAX_RTP_SEQ 16
+
+struct rtp_seq{
+ unsigned short rtp_data[MAX_RTP_SEQ]; //RTPTime<<8||RTPAmp
+ int rtp_counts;
+ int rtp_index;
+};
+
+struct drv2605l_fw_header{
+ int fw_magic;
+ int fw_size;
+ int fw_date;
+ int fw_chksum;
+ int fw_effCount;
+};
+
+#define DRV2605L_MAGIC 0x2605
+
+struct drv2605l_data {
+ struct drv2605l_platform_data platform_data;
+ unsigned 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 is_playing;
+ char r_buff[MAX_READ_BYTES];
+ int r_len;
+
+ int vibration_time;
+ int silience_time;
+ char repeat_times;
+ volatile char work_mode;
+ char dev_mode;
+
+ struct rtp_seq rtp_seq;
+
+ int otp;
+
+ struct wake_lock wk_lock;
+ 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;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+
+ struct drv2605l_fw_header fw_header;
+};
+
+#endif
+