diff options
Diffstat (limited to 'drivers/input/misc')
-rw-r--r-- | drivers/input/misc/Kconfig | 8 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 2 | ||||
-rw-r--r-- | drivers/input/misc/hall_switch.c | 269 | ||||
-rw-r--r-- | drivers/input/misc/vibrator/Kconfig | 13 | ||||
-rw-r--r-- | drivers/input/misc/vibrator/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/vibrator/drv2605l.c | 1125 | ||||
-rw-r--r-- | drivers/input/misc/vibrator/drv2605l.h | 440 |
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 + |