| #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 <linux/miscdevice.h> |
| #include <linux/zh915.h> |
| #include <linux/of.h> |
| #include <linux/irq.h> |
| #include <linux/interrupt.h> |
| #include "../staging/android/timed_output.h" |
| #include <linux/sec_sysfs.h> |
| #include <linux/kthread.h> |
| #include <linux/of_gpio.h> |
| #include <linux/printk.h> |
| #if defined(CONFIG_SSP_MOTOR_CALLBACK) |
| #include <linux/ssp_motorcallback.h> |
| #endif |
| |
| static struct zh915_data *g_drvdata; |
| |
| static int zh915_reg_read(struct zh915_data *zh915data, unsigned char reg) |
| { |
| unsigned int val; |
| int ret; |
| |
| ret = regmap_read(zh915data->mpRegmap, reg, &val); |
| |
| if (ret < 0){ |
| dev_err(zh915data->dev, |
| "%s reg=0x%x error %d\n", __FUNCTION__, reg, ret); |
| return ret; |
| } |
| else |
| return val; |
| } |
| |
| static int zh915_reg_write(struct zh915_data *zh915data, |
| unsigned char reg, unsigned char val) |
| { |
| int ret; |
| |
| ret = regmap_write(zh915data->mpRegmap, reg, val); |
| if (ret < 0){ |
| dev_err(zh915data->dev, |
| "%s reg=0x%x, value=0%x error %d\n", |
| __FUNCTION__, reg, val, ret); |
| } |
| |
| return ret; |
| } |
| |
| static int vibrator_get_time(struct timed_output_dev *dev) |
| { |
| struct zh915_data *zh915data = container_of(dev, struct zh915_data, to_dev); |
| |
| if (hrtimer_active(&zh915data->timer)) { |
| ktime_t r = hrtimer_get_remaining(&zh915data->timer); |
| return ktime_to_ms(r); |
| } |
| |
| return 0; |
| } |
| |
| static void zh915_stop(struct zh915_data *zh915data) |
| { |
| if(zh915data->running == YES) { |
| hrtimer_cancel(&zh915data->timer); |
| |
| zh915_reg_write(zh915data, REG_MODE, STOP_MODE); |
| |
| zh915data->running = NO; |
| wake_unlock(&zh915data->wklock); |
| pr_info("[VIB] %s\n", __func__); |
| } |
| } |
| |
| static int zh915_haptic_set_frequency(struct zh915_data *zh915data, int num) |
| { |
| int set_reg; |
| int h_freq = 0; |
| |
| if (num >= 0 && num < zh915data->multi_frequency) { |
| zh915data->freq = zh915data->multi_freq[num]; |
| zh915data->duty = zh915data->multi_duty[num]; |
| zh915data->freq_num = num; |
| |
| // printk("[VIB] %s: freq_num=%d, RESONANCE FREQUENCY: 0x%x\n", __func__, num, zh915data->freq); |
| |
| } else if (num >= 1200 && num <= 3500) { |
| |
| /** |
| * Calc frequency |
| * Resonace_Rrequency[7:0] = (12.5Mhz/Frequency - 50000)/512 |
| * freq_num = 1650 : 165.0 Hz |
| */ |
| h_freq = num / 10; |
| set_reg = (12500000/h_freq - 50000) / 512; |
| |
| zh915data->freq = set_reg; |
| /* Set default duty value */ |
| if (zh915data->overdrive_state) |
| zh915data->duty = zh915data->multi_duty[HOMEKEY_PRESS_FREQ]; |
| else |
| zh915data->duty = zh915data->multi_duty[0]; |
| |
| // printk("[VIB] %s: REG_RESONANCE_FREQ: 0x%x : strength = 0x%x \n", __func__, zh915data->freq, zh915data->duty); |
| } else { |
| pr_err("%s out of range %d\n", __func__, num); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /* function paramter : intensity meas required strength value */ |
| /* Max resolution is 10000 */ |
| |
| static int zh915_haptic_set_intensity(struct zh915_data *zh915data, int intensity) |
| { |
| u8 duty = zh915data->duty; |
| int req_duty = 0; |
| |
| if (intensity < -(MAX_INTENSITY) || MAX_INTENSITY < intensity) { |
| pr_err("%s out of range %d\n", __func__, intensity); |
| return -EINVAL; |
| } |
| |
| if (MAX_INTENSITY == intensity) |
| req_duty = duty; |
| else if (intensity == -(MAX_INTENSITY)) |
| req_duty = ~duty; |
| else if (0 != intensity) { |
| req_duty = duty * abs(intensity) / MAX_INTENSITY; |
| //duty = (u8)((ZH915_MAX_DEC * req_duty / 100) +1); |
| |
| if (intensity < 0) |
| req_duty = ~ req_duty; |
| } |
| |
| // printk("[VIB] %s : intensity = 0x%x /duty = 0x%x / req_duty = 0x%x \n", __func__, intensity, (unsigned int)duty, req_duty); |
| if (intensity == 0) |
| req_duty = 0x00; |
| |
| zh915data->intensity = intensity; |
| zh915data->duty = req_duty; |
| |
| return 0; |
| } |
| |
| static int zh915_haptic_set_overdrive_state(struct zh915_data *zh915data, int overdrive) |
| { |
| if(overdrive) |
| zh915data->overdrive_state = true; |
| else |
| zh915data->overdrive_state = false; |
| return 0; |
| } |
| |
| static void zh915_haptic_engine_set_packet(struct zh915_data *zh915data, |
| struct vib_packet haptic_packet) |
| { |
| int frequency = haptic_packet.freq; |
| int intensity = haptic_packet.intensity; |
| int overdrive = haptic_packet.overdrive; |
| int prev_packet=0; |
| |
| if (!zh915data->f_packet_en) { |
| pr_err("[VIB] haptic packet is empty\n"); |
| return; |
| } |
| |
| zh915_haptic_set_overdrive_state(zh915data, overdrive); |
| zh915_haptic_set_frequency(zh915data, frequency); |
| zh915_haptic_set_intensity(zh915data, intensity); |
| |
| if (intensity == 0) { |
| if (zh915data->packet_running) { |
| // pr_info("[VIB] haptic engine: motor stop\n"); |
| zh915_reg_write(zh915data, REG_MODE, STOP_MODE); |
| } |
| zh915data->packet_running = false; |
| } else { |
| if (!zh915data->packet_running) { |
| // pr_info("[VIB] haptic engine [%d] : motor run : freq=0x%x, duty = 0x%x\n", zh915data->packet_cnt, zh915data->freq, zh915data->duty); |
| |
| zh915_reg_write(zh915data, REG_CONTROL, CONTROL_LOOP_MASK); // LPA, LOOP OPEN |
| zh915_reg_write(zh915data, REG_RESONANCE_FREQ, zh915data->freq); // Frequency set |
| zh915_reg_write(zh915data, REG_STRENGTH_WRITE, zh915data->duty); // strength set |
| zh915_reg_write(zh915data, REG_MODE, I2C_MODE); |
| } else { |
| zh915_reg_write(zh915data, REG_RESONANCE_FREQ, zh915data->freq); // Frequency set |
| if (zh915data->packet_cnt) |
| prev_packet = zh915data->packet_cnt -1; |
| if (intensity != zh915data->test_pac[prev_packet].intensity) |
| zh915_reg_write(zh915data, REG_STRENGTH_WRITE, zh915data->duty); // strength set |
| } |
| zh915data->packet_running = true; |
| } |
| |
| pr_info("[VIB] haptic_engine : freq:%d, intensity:%d, time:%d overdrive: %d\n", frequency, intensity, zh915data->timeout, zh915data->overdrive_state); |
| } |
| |
| static ssize_t haptic_engine_store(struct device *dev, |
| struct device_attribute *devattr, const char *buf, size_t count) |
| { |
| struct timed_output_dev *to_dev = dev_get_drvdata(dev); |
| struct zh915_data *zh915data |
| = container_of(to_dev, struct zh915_data, to_dev); |
| int i = 0, _data = 0, tmp = 0; |
| |
| if (buf == NULL) { |
| pr_err("%s, buf is NULL, Please check packet data again\n", |
| __func__); |
| zh915data->f_packet_en = false; |
| return count; |
| } |
| |
| if (sscanf(buf, "%d", &_data) == 1) { |
| if (_data > PACKET_MAX_SIZE * 4) |
| pr_info("%s, [%d] packet size over\n", __func__, _data); |
| else { |
| zh915data->packet_size = _data / 4; |
| zh915data->packet_cnt = 0; |
| zh915data->f_packet_en = true; |
| |
| buf = strstr(buf, " "); |
| |
| for (i = 0; i < zh915data->packet_size; i++) { |
| for (tmp = 0; tmp < 4; tmp++) { |
| if (sscanf(buf++, "%d", &_data) == 1) { |
| switch (tmp){ |
| case 0: |
| zh915data->test_pac[i].time = _data; |
| break; |
| case 1: |
| zh915data->test_pac[i].intensity = _data; |
| break; |
| case 2: |
| zh915data->test_pac[i].freq = _data; |
| break; |
| case 3: |
| zh915data->test_pac[i].overdrive = _data; |
| break; |
| } |
| buf = strstr(buf, " "); |
| } else { |
| pr_info("%s packet data error\n", __func__); |
| zh915data->f_packet_en = false; |
| return count; |
| } |
| } |
| } |
| } |
| } |
| |
| return count; |
| } |
| |
| static ssize_t force_touch_intensity_store(struct device *dev, |
| struct device_attribute *devattr, const char *buf, size_t count) |
| { |
| struct timed_output_dev *tdev = dev_get_drvdata(dev); |
| struct zh915_data *zh915data |
| = container_of(tdev, struct zh915_data, to_dev); |
| int intensity = 0, ret = 0; |
| |
| ret = kstrtoint(buf, 0, &intensity); |
| if (ret) { |
| pr_err("fail to get intensity\n"); |
| return -EINVAL; |
| } |
| |
| pr_info("[VIB] %s %d\n", __func__, intensity); |
| |
| zh915data->force_touch_intensity = intensity; |
| ret = zh915_haptic_set_intensity(zh915data, intensity); |
| if (ret) |
| return ret; |
| |
| return count; |
| } |
| |
| static ssize_t force_touch_intensity_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct timed_output_dev *tdev = dev_get_drvdata(dev); |
| struct zh915_data *zh915data |
| = container_of(tdev, struct zh915_data, to_dev); |
| |
| pr_info("[VIB] %s %d\n", __func__, zh915data->force_touch_intensity); |
| |
| return snprintf(buf, VIB_BUFSIZE, "force touch intensity: %u\n", zh915data->force_touch_intensity); |
| } |
| |
| static DEVICE_ATTR(force_touch_intensity, 0660, force_touch_intensity_show, force_touch_intensity_store); |
| |
| static ssize_t haptic_engine_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct timed_output_dev *to_dev = dev_get_drvdata(dev); |
| struct zh915_data *zh915data |
| = container_of(to_dev, struct zh915_data, to_dev); |
| int i = 0; |
| char *bufp = buf; |
| |
| bufp += snprintf(bufp, VIB_BUFSIZE, "\n"); |
| for (i = 0; i < zh915data->packet_size && zh915data->f_packet_en; i++) { |
| bufp+= snprintf(bufp, VIB_BUFSIZE, "%u,", zh915data->test_pac[i].time); |
| bufp+= snprintf(bufp, VIB_BUFSIZE, "%u,", zh915data->test_pac[i].intensity); |
| bufp+= snprintf(bufp, VIB_BUFSIZE, "%u,", zh915data->test_pac[i].freq); |
| bufp+= snprintf(bufp, VIB_BUFSIZE, "%u,", zh915data->test_pac[i].overdrive); |
| } |
| bufp += snprintf(bufp, VIB_BUFSIZE, "\n"); |
| |
| return strlen(buf); |
| } |
| |
| static DEVICE_ATTR(haptic_engine, 0660, haptic_engine_show, haptic_engine_store); |
| |
| static void vibrator_enable( struct timed_output_dev *dev, int value) |
| { |
| struct zh915_data *zh915data = |
| container_of(dev, struct zh915_data, to_dev); |
| struct pinctrl *i2c_pinctrl; |
| int cnt = 1000; |
| |
| pr_info("[VIB] %s %dms\n", __func__, value); |
| |
| flush_kthread_worker(&zh915data->kworker); |
| hrtimer_cancel(&zh915data->timer); |
| |
| mutex_lock(&zh915data->lock); |
| |
| i2c_pinctrl = devm_pinctrl_get_select(zh915data->dev, "motor_en_high"); |
| if (IS_ERR(i2c_pinctrl)) |
| dev_err(zh915data->dev, "[VIB]: %s : motor_en_high / could not set motor_en pins\n",__func__); |
| // usleep_range(1 * 1000, 1 * 1000); |
| |
| value = min_t(int, value, MAX_TIMEOUT); |
| zh915data->timeout = value; |
| zh915data->packet_running = false; |
| |
| if (value > 0) { |
| wake_lock(&zh915data->wklock); |
| |
| gpio_direction_output(zh915data->boost_en, 1); |
| while (!gpio_get_value(zh915data->boost_en)) |
| { |
| mdelay(1); |
| if (--cnt < 0 ) |
| { |
| printk("[VIB] : %s : boost_en high fail \n",__func__); |
| break; |
| } |
| } |
| /* zh915 startup time need */ |
| msleep(5); |
| |
| if (zh915data->f_packet_en) { |
| zh915data->timeout = zh915data->test_pac[0].time; |
| zh915_haptic_engine_set_packet(zh915data, zh915data->test_pac[0]); |
| #if defined(CONFIG_SSP_MOTOR_CALLBACK) |
| if(zh915data->intensity == 0) |
| setSensorCallback(false, 0); |
| else |
| setSensorCallback(true, zh915data->timeout); |
| #endif |
| } else { |
| zh915data->running = YES; |
| // intensity |
| // Frequency write |
| zh915_reg_write(zh915data, REG_CONTROL, CONTROL_LOOP_MASK); // LPA, LOOP OPEN |
| zh915_reg_write(zh915data, REG_RESONANCE_FREQ, zh915data->freq); // Frequency set |
| zh915_reg_write(zh915data, REG_STRENGTH_WRITE, zh915data->duty); // strength set |
| zh915_reg_write(zh915data, REG_MODE, I2C_MODE & MODE_MASK); // motor enable |
| #if defined(CONFIG_SSP_MOTOR_CALLBACK) |
| setSensorCallback(true, value); |
| #endif |
| printk("[VIB] %s: motor on : frequency = 0x%x, duty = 0x%x, intensity = 0x%x, freq_num = %d \n",__func__, zh915data->freq, zh915data->duty, zh915data->intensity, zh915data->freq_num); |
| } |
| pr_info("[VIB] timer setting\n"); |
| hrtimer_start(&zh915data->timer, |
| ns_to_ktime((u64)zh915data->timeout * NSEC_PER_MSEC), HRTIMER_MODE_REL); |
| } |
| else if (value == 0) { |
| zh915data->f_packet_en = false; |
| zh915data->packet_cnt = 0; |
| zh915data->packet_size = 0; |
| |
| if (zh915data->running == YES){ |
| pr_info("[VIB] turn OFF\n"); |
| zh915_stop(zh915data); |
| } |
| else{ |
| pr_info("[VIB] already OFF\n"); |
| } |
| #if defined(CONFIG_SSP_MOTOR_CALLBACK) |
| setSensorCallback(false, 0); |
| #endif |
| gpio_direction_output(zh915data->boost_en, 0); |
| } |
| |
| mutex_unlock(&zh915data->lock); |
| } |
| |
| static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) |
| { |
| struct zh915_data *zh915data = |
| container_of(timer, struct zh915_data, timer); |
| pr_info("[VIB] %s\n", __func__); |
| |
| queue_kthread_work(&zh915data->kworker, &zh915data->vibrator_work); |
| |
| return HRTIMER_NORESTART; |
| } |
| |
| static void vibrator_work_routine(struct kthread_work *work) |
| { |
| struct zh915_data *zh915data = |
| container_of(work, struct zh915_data, vibrator_work); |
| struct hrtimer *timer = &zh915data->timer; |
| pr_info("[VIB] %s\n", __func__); |
| |
| mutex_lock(&zh915data->lock); |
| |
| if (zh915data->f_packet_en) { |
| if (++zh915data->packet_cnt >= zh915data->packet_size) { |
| zh915data->f_packet_en = false; |
| zh915data->packet_cnt = 0; |
| zh915data->packet_size = 0; |
| } else { |
| zh915_haptic_engine_set_packet(zh915data, zh915data->test_pac[zh915data->packet_cnt]); |
| #if defined(CONFIG_SSP_MOTOR_CALLBACK) |
| if(drvdata->intensity == 0) |
| setSensorCallback(false, 0); |
| else |
| setSensorCallback(true, zh915data->test_pac[zh915data->packet_cnt].time); |
| #endif |
| hrtimer_start(timer, ns_to_ktime((u64)zh915data->test_pac[zh915data->packet_cnt].time * NSEC_PER_MSEC), HRTIMER_MODE_REL); |
| goto unlock; |
| } |
| } |
| |
| zh915_stop(zh915data); |
| gpio_direction_output(zh915data->boost_en, 0); |
| #if defined(CONFIG_SSP_MOTOR_CALLBACK) |
| setSensorCallback(false,0); |
| #endif |
| unlock: |
| mutex_unlock(&zh915data->lock); |
| } |
| |
| static int Haptics_init(struct zh915_data *zh915data) |
| { |
| int ret = 0; |
| struct task_struct *kworker_task; |
| |
| zh915data->to_dev.name = "vibrator"; |
| zh915data->to_dev.get_time = vibrator_get_time; |
| zh915data->to_dev.enable = vibrator_enable; |
| |
| ret = timed_output_dev_register(&(zh915data->to_dev)); |
| if ( ret < 0){ |
| dev_err(zh915data->dev, |
| "zh915: fail to create timed output dev\n"); |
| return ret; |
| } |
| |
| hrtimer_init(&zh915data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
| zh915data->timer.function = vibrator_timer_func; |
| |
| init_kthread_worker(&zh915data->kworker); |
| kworker_task = kthread_run(kthread_worker_fn, |
| &zh915data->kworker, "zh915_haptic"); |
| if (IS_ERR(kworker_task)) { |
| pr_err("Failed to create message pump task\n"); |
| } |
| init_kthread_work(&zh915data->vibrator_work, vibrator_work_routine); |
| |
| wake_lock_init(&zh915data->wklock, WAKE_LOCK_SUSPEND, "vibrator"); |
| mutex_init(&zh915data->lock); |
| |
| return 0; |
| } |
| |
| static struct regmap_config zh915_i2c_regmap = { |
| .reg_bits = 8, |
| .val_bits = 8, |
| .cache_type = REGCACHE_NONE, |
| }; |
| |
| static void zh915_reg_init(struct zh915_data *zh915data) { |
| |
| int val; |
| |
| // zh915_reg_write(zh915data, REG_MODE, I2C_MODE & MODE_MASK); |
| zh915_reg_write(zh915data, REG_CONTROL, CONTROL_LOOP_MASK); |
| /* Power Calibration disable, Open Loop, |
| * Brake disable, LRA motor |
| */ |
| zh915_reg_write(zh915data, REG_RESONANCE_FREQ, 0x33); // 164Hz |
| zh915_reg_write(zh915data, REG_STRENGTH_WRITE, 0x78); // 2.0Vrms |
| |
| val = zh915_reg_read(zh915data, REG_CONTROL); |
| printk("[VIB] : %s : zh915 REG_CONTROL(0x02) = 0x%x \n", __func__, val); |
| val = zh915_reg_read(zh915data, REG_RESONANCE_FREQ); |
| printk("[VIB] : %s : zh915 REG_RESONANCE_FREQ(0x%x) = 0x%x \n", __func__, REG_RESONANCE_FREQ, val); |
| val = zh915_reg_read(zh915data, REG_STRENGTH_READ); |
| printk("[VIB] : %s : zh915 REG_STRENGTH_READ(0x%x) = 0x%x \n", __func__, REG_STRENGTH_READ, val); |
| } |
| |
| static ssize_t set_chip_register(struct device *dev, |
| struct device_attribute *devattr, const char *buf, size_t count) |
| { |
| struct zh915_data *zh915data = dev_get_drvdata(dev); |
| char buff[10] = {0,}; |
| int cnt, ret; |
| u16 reg; |
| u8 set_reg; |
| u8 value, val; |
| |
| cnt = count; |
| cnt = (buf[cnt-1] == '\n') ? cnt-1 : 9; |
| memcpy(buff, buf, cnt); |
| buff[cnt] = '\0'; |
| |
| ret = kstrtou16(buff, 0, ®); |
| if (ret != 0) { |
| dev_err(dev, "[VIB] fail to get period.\n"); |
| return count; |
| } |
| set_reg = (u8)((reg & 0xFF00) >> 8); |
| value =(u8)(reg & 0xFF); |
| |
| zh915_reg_write(zh915data, set_reg, value); |
| |
| if (set_reg == 0x00) |
| { |
| // Print Current Setting value |
| val = zh915_reg_read(zh915data, REG_CONTROL); |
| printk("[VIB] : %s : zh915 REG_CONTROL(0x02) = 0x%x \n", __func__, val); |
| val = zh915_reg_read(zh915data, REG_RESONANCE_FREQ); |
| printk("[VIB] : %s : zh915 REG_RESONANCE_FREQ(0x%x) = 0x%x \n", __func__, REG_RESONANCE_FREQ, val); |
| val = zh915_reg_read(zh915data, REG_STRENGTH_READ); |
| printk("[VIB] : %s : zh915 REG_STRENGTH_READ(0x%x) = 0x%x \n", __func__, REG_STRENGTH_READ, val); |
| |
| val = zh915_reg_read(zh915data, REG_MODE); |
| printk("[VIB] : %s : zh915 REG_MODE(0x%x) = 0x%x \n", __func__, REG_MODE, val); |
| |
| } |
| printk("[VIB] Set Register : 0x%x Value : 0x%x\n", set_reg, value); |
| |
| return count; |
| } |
| |
| static ssize_t get_chip_register(struct device *dev, |
| struct device_attribute *devattr, const char *buf, size_t count) |
| { |
| struct zh915_data *zh915data = dev_get_drvdata(dev); |
| char buff[10] = {0,}; |
| int cnt, ret; |
| u8 reg; |
| int value; |
| |
| cnt = count; |
| cnt = (buf[cnt-1] == '\n') ? cnt-1 : cnt; |
| memcpy(buff, buf, cnt); |
| buff[cnt] = '\0'; |
| |
| ret = kstrtou8(buff, 0, ®); |
| if (ret != 0) { |
| dev_err(dev, "[VIB] fail to get register.\n"); |
| return count; |
| } |
| zh915data->ic_reg_address = reg; |
| value = zh915_reg_read(zh915data, reg); |
| printk("[VIB] Get Register : 0x%x Value : 0x%x\n", reg, value); |
| |
| return count; //sprintf(buf, "Register : 0x%x Value : 0x%x\n", reg, value); |
| } |
| |
| static ssize_t init_chip_register(struct device *dev, |
| struct device_attribute *devattr, char *buf) |
| |
| { |
| struct zh915_data *zh915data = dev_get_drvdata(dev); |
| |
| zh915_reg_init(zh915data); |
| |
| printk("[VIB] Init Register\n"); |
| |
| return sprintf(buf, "Init success\n"); |
| } |
| |
| static ssize_t zh915_power_control(struct device *dev, |
| struct device_attribute *devattr, const char *buf, size_t count) |
| { |
| u16 val; |
| int ret; |
| struct zh915_data *zh915data = dev_get_drvdata(dev); |
| |
| ret = kstrtou16(buf, 0, &val); |
| if (val) |
| { |
| gpio_direction_output(zh915data->boost_en, 1); |
| while (!gpio_get_value(zh915data->boost_en)); |
| printk("[VIB] %s : motor_boost_en high \n",__func__); |
| mdelay(5); |
| } else { |
| gpio_direction_output(zh915data->boost_en, 0); |
| while (gpio_get_value(zh915data->boost_en)); |
| printk("[VIB] %s : motor_boost_en Low \n",__func__); |
| } |
| return count; |
| } |
| |
| static ssize_t zh915_power_cntrol_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct zh915_data *zh915data = dev_get_drvdata(dev); |
| int value; |
| |
| value = gpio_get_value(zh915data->boost_en); |
| printk("[VIB] %s : mot_boost_en gpio Value : 0x%x\n", __func__, value); |
| |
| return sprintf(buf, "mot_boost_en gpio Value : 0x%x\n",value); |
| } |
| |
| static ssize_t get_register_data(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct zh915_data *zh915data = dev_get_drvdata(dev); |
| int value; |
| |
| if ( zh915data->ic_reg_address == 0x01) |
| zh915data->ic_reg_address = 0x0c; |
| value = zh915_reg_read(zh915data, zh915data->ic_reg_address); |
| printk("[VIB] Get Register : 0x%x Value : 0x%x\n", zh915data->ic_reg_address, value); |
| |
| return sprintf(buf, "Register : 0x%x Value : 0x%x\n", zh915data->ic_reg_address, value); |
| } |
| |
| /* Force touch */ |
| static ssize_t store_duty(struct device *dev, |
| struct device_attribute *devattr, const char *buf, size_t count) |
| { |
| struct zh915_data *zh915data = dev_get_drvdata(dev); |
| |
| int ret; |
| u16 duty; |
| |
| ret = kstrtou16(buf, 0, &duty); |
| if (ret != 0) { |
| dev_err(dev, "fail to get duty.\n"); |
| return count; |
| } |
| |
| if (duty > 100) |
| duty = 100; |
| zh915data->duty = 127 * duty / 100 + 1; |
| printk("[VIB] %s : duty = 0x%x , strength = 0x%x \n",__func__, duty, (u32)zh915data->duty); |
| return count; |
| } |
| |
| static ssize_t store_frequency(struct device *dev, |
| struct device_attribute *devattr, const char *buf, size_t count) |
| { |
| struct zh915_data *zh915data |
| = dev_get_drvdata(dev); |
| |
| int ret; |
| u16 freq; |
| int temp; |
| |
| ret = kstrtou16(buf, 0, &freq); |
| if (ret != 0) { |
| dev_err(dev, "fail to get period.\n"); |
| return count; |
| } |
| |
| if (freq <= 120) |
| freq = 120; |
| else if (freq >= 350) |
| freq = 350; |
| |
| temp = (12500000/freq - 50000) / 512; |
| zh915data->freq = temp; |
| printk("[VIB] %s : req_freq = 0x%x / setting value = 0x%x\n",__func__, freq, (u32)zh915data->freq); |
| |
| return count; |
| } |
| |
| static ssize_t show_duty_frequency(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct zh915_data *zh915data = dev_get_drvdata(dev); |
| |
| printk("[VIB] %s : duty = 0x%x, period = 0x%x \n",__func__, (u32)zh915data->duty, (u32)zh915data->freq); |
| |
| return snprintf(buf, VIB_BUFSIZE, "duty: 0x%x, period: 0x%x\n", |
| (u32)zh915data->duty, |
| (u32)zh915data->freq); |
| } |
| |
| static DEVICE_ATTR(set_register, 0220, NULL, set_chip_register); |
| static DEVICE_ATTR(get_register, 0660, get_register_data, get_chip_register); |
| static DEVICE_ATTR(init_register, 0440, init_chip_register, NULL); |
| static DEVICE_ATTR(power_control, 0660, zh915_power_cntrol_show, zh915_power_control); |
| static DEVICE_ATTR(set_duty, 0220, NULL, store_duty); |
| static DEVICE_ATTR(set_frequency, 0220, NULL, store_frequency); |
| static DEVICE_ATTR(show_duty_frequency, 0440, show_duty_frequency, NULL); |
| |
| static struct attribute *sec_motor_attributes[] = { |
| &dev_attr_set_register.attr, |
| &dev_attr_get_register.attr, |
| &dev_attr_init_register.attr, |
| &dev_attr_power_control.attr, |
| &dev_attr_set_duty.attr, |
| &dev_attr_set_frequency.attr, |
| &dev_attr_show_duty_frequency.attr, |
| NULL, |
| }; |
| |
| static struct attribute_group sec_motor_attr_group = { |
| .attrs = sec_motor_attributes, |
| }; |
| |
| static ssize_t intensity_store(struct device *dev, |
| struct device_attribute *devattr, const char *buf, size_t count) |
| { |
| struct timed_output_dev *t_dev = dev_get_drvdata(dev); |
| struct zh915_data *zh915data = container_of(t_dev, struct zh915_data, to_dev); |
| int intensity = 0, ret = 0; |
| |
| |
| ret = kstrtoint(buf, 0, &intensity); |
| if (ret) { |
| pr_err("[VIB] fail to get intensity\n"); |
| return -EINVAL; |
| } |
| |
| if ((intensity < 0) || (MAX_INTENSITY < intensity)) { |
| pr_err("[VIB] out of rage\n"); |
| return -EINVAL; |
| } |
| |
| zh915data->intensity = intensity; |
| printk("[VIB] %s : Set intensity : %d\n",__func__, zh915data->intensity); |
| |
| zh915_haptic_set_intensity(zh915data, zh915data->intensity); |
| |
| return count; |
| } |
| |
| static ssize_t intensity_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct timed_output_dev *t_dev = dev_get_drvdata(dev); |
| struct zh915_data *zh915data = container_of(t_dev, struct zh915_data, to_dev); |
| |
| return snprintf(buf, VIB_BUFSIZE, "[VIB] %s : intensity: %d\n",__func__, zh915data->intensity); |
| } |
| |
| static DEVICE_ATTR(intensity, 0660, intensity_show, intensity_store); |
| |
| |
| static ssize_t frequency_store(struct device *dev, |
| struct device_attribute *devattr, const char *buf, size_t count) |
| { |
| struct timed_output_dev *t_dev = dev_get_drvdata(dev); |
| struct zh915_data *zh915data = container_of(t_dev, struct zh915_data, to_dev); |
| int freq_num = 0, ret = 0; |
| |
| ret = kstrtoint(buf, 0, &freq_num); |
| if (ret) { |
| pr_err("[VIB] fail to get frequency\n"); |
| return -EINVAL; |
| } |
| |
| if (freq_num < 0 || freq_num >= zh915data->multi_frequency) { |
| pr_err("out of rage\n"); |
| return -EINVAL; |
| } |
| |
| zh915data->freq_num = freq_num; |
| printk("[VIB] %s : freq_num: %d\n", __func__, zh915data->freq_num); |
| |
| zh915_haptic_set_frequency(zh915data, zh915data->freq_num); |
| |
| return count; |
| } |
| |
| static ssize_t frequency_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct timed_output_dev *t_dev = dev_get_drvdata(dev); |
| struct zh915_data *zh915data = container_of(t_dev, struct zh915_data, to_dev); |
| return snprintf(buf, VIB_BUFSIZE, "[VIB] %s : freq_num: %d\n",__func__, zh915data->freq_num); |
| } |
| |
| static DEVICE_ATTR(multi_freq, 0660, frequency_show, frequency_store); |
| |
| #ifdef CONFIG_OF |
| static int zh915_parse_dt(struct device *dev, |
| struct zh915_data *zh915data) |
| { |
| struct device_node *np_root = dev->parent->of_node; |
| struct device_node *np = dev->of_node; |
| int ret; |
| u32 temp; |
| int i; |
| |
| zh915data->boost_en = of_get_named_gpio(np, "mot_boost_en", 0); |
| pr_info("[VIB] %s : motor_mode zh915data->boost_en gpio = %d\n", __func__, zh915data->boost_en); |
| |
| np = of_find_node_by_name(np_root, |
| "motor"); |
| |
| if (np == NULL) { |
| pr_err("%s : error to get dt node\n", __func__); |
| goto err_parsing_dt; |
| } |
| |
| ret = of_property_read_u32(np, "motor,motor_type", &temp); |
| if (IS_ERR_VALUE(ret)) { |
| pr_err("%s : error to get dt node motor_type \n", __func__); |
| goto err_parsing_dt; |
| } else if (temp != EXTERNAL_DRIVING_IC) { |
| printk("[VIB] %s : IFPMIC use case : zh915 probe error return : motor type = %d\n",__func__, temp); |
| goto err_parsing_dt; |
| } else |
| printk("[VIB] : %s : motor_type value %d \n", __func__, temp); |
| |
| np = of_find_node_by_name(np_root, |
| "haptic"); |
| |
| if (np == NULL) { |
| pr_err("%s : error to get dt node\n", __func__); |
| goto err_parsing_dt; |
| } |
| |
| ret = of_property_read_u32(np, "haptic,multi_frequency", &temp); |
| if (IS_ERR_VALUE(ret)) { |
| pr_err("%s : error to get dt node multi_frequency\n", __func__); |
| zh915data->multi_frequency = 0; |
| } else |
| zh915data->multi_frequency = (int)temp; |
| |
| if (zh915data->multi_frequency) { |
| zh915data->multi_freq |
| = devm_kzalloc(dev, sizeof(u32)*zh915data->multi_frequency, GFP_KERNEL); |
| if (!zh915data->multi_freq) { |
| pr_err("%s: failed to allocate freq data\n", __func__); |
| goto err_parsing_dt; |
| } |
| |
| zh915data->multi_duty |
| = devm_kzalloc(dev, sizeof(u32)*zh915data->multi_frequency, GFP_KERNEL); |
| if (!zh915data->multi_duty) { |
| pr_err("%s: failed to allocate duty data \n", __func__); |
| goto err_parsing_dt_freq; |
| } |
| |
| ret = of_property_read_u32_array(np, "haptic,freq", zh915data->multi_freq, |
| zh915data->multi_frequency); |
| if (IS_ERR_VALUE(ret)) { |
| pr_err("%s : error to get dt freq data\n", __func__); |
| goto err_parsing_dt_duty; |
| } |
| |
| ret = of_property_read_u32_array(np, "haptic,duty", zh915data->multi_duty, |
| zh915data->multi_frequency); |
| if (IS_ERR_VALUE(ret)) { |
| pr_err("%s : error to get dt duty data\n", __func__); |
| goto err_parsing_dt_duty; |
| } |
| zh915data->duty = zh915data->multi_duty[0]; |
| zh915data->freq = zh915data->multi_freq[0]; |
| zh915data->freq_num = 0; |
| } |
| |
| /* debugging */ |
| if (zh915data->multi_frequency) { |
| pr_debug("multi frequency = %d\n", zh915data->multi_frequency); |
| for (i = 0; i < zh915data->multi_frequency; i++) { |
| pr_debug("freq[%d] = %d\n", i, zh915data->multi_freq[i]); |
| } |
| } |
| |
| return 0; |
| |
| err_parsing_dt_duty: |
| kfree(zh915data->multi_duty); |
| err_parsing_dt_freq: |
| kfree(zh915data->multi_freq); |
| err_parsing_dt: |
| return -ENODEV; |
| } |
| #endif |
| |
| static int zh915_probe(struct i2c_client* client, const struct i2c_device_id* id) |
| { |
| struct zh915_data *zh915data; |
| int err = 0, ret = 0; |
| |
| pr_info("[VIB] %s\n", __func__); |
| |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) |
| { |
| dev_err(&client->dev, "%s:I2C check failed\n", __FUNCTION__); |
| return -ENODEV; |
| } |
| |
| zh915data = devm_kzalloc(&client->dev, sizeof(struct zh915_data), GFP_KERNEL); |
| if (zh915data == NULL){ |
| dev_err(&client->dev, "%s:no memory\n", __FUNCTION__); |
| return -ENOMEM; |
| } |
| |
| zh915data->dev = &client->dev; |
| zh915data->i2c = client; |
| zh915data->mpRegmap = devm_regmap_init_i2c(client, &zh915_i2c_regmap); |
| if (IS_ERR(zh915data->mpRegmap)) { |
| err = PTR_ERR(zh915data->mpRegmap); |
| dev_err(zh915data->dev, |
| "%s:Failed to allocate register map: %d\n",__FUNCTION__,err); |
| goto exit_probe; |
| } |
| |
| ret = zh915_parse_dt(&client->dev, zh915data); |
| if (ret) { |
| pr_err("[%s] zh915 parse dt failed\n", __func__); |
| err = -ENODEV; |
| goto exit_probe; |
| } |
| |
| if (gpio_is_valid(zh915data->boost_en)) { |
| ret = gpio_request(zh915data->boost_en, "zh915_power_en"); |
| if (ret) |
| { |
| dev_err(zh915data->dev, "[VIB]: %s : motor enable fail \n",__func__); |
| goto exit_sec_devices; |
| } |
| ret = gpio_direction_output(zh915data->boost_en, 1); |
| |
| if (ret) { |
| dev_err(zh915data->dev, "[VIB]: Unable to set zh915_power_en: output direction, error %d\n", ret); |
| goto exit_sec_devices; |
| } |
| // zh915 ic need to startup time |
| msleep(2); |
| } else { |
| dev_err(zh915data->dev, "[VIB]: %s: gpio is invalid (%d)\n", __func__, zh915data->boost_en); |
| } |
| |
| zh915data->f_packet_en = false; |
| zh915data->packet_cnt = 0; |
| zh915data->packet_size = 0; |
| zh915data->force_touch_intensity = -1; |
| |
| ret = Haptics_init(zh915data); |
| if (ret<0) { |
| goto exit_sec_devices; |
| } |
| |
| err = sysfs_create_file(&zh915data->to_dev.dev->kobj, |
| &dev_attr_intensity.attr); |
| if (err < 0) { |
| pr_err("Failed to register sysfs : %d\n", err); |
| goto err_timed_output_register; |
| } |
| |
| err = sysfs_create_file(&zh915data->to_dev.dev->kobj, |
| &dev_attr_multi_freq.attr); |
| if (err < 0) { |
| pr_err("Failed to register multi_freq sysfs : %d\n", err); |
| goto err_timed_output_register; |
| } |
| err = sysfs_create_file(&zh915data->to_dev.dev->kobj, |
| &dev_attr_haptic_engine.attr); |
| if (err < 0) { |
| pr_err("Failed to register haptic_engine sysfs : %d\n", err); |
| goto err_timed_output_register; |
| } |
| |
| err = sysfs_create_file(&zh915data->to_dev.dev->kobj, |
| &dev_attr_force_touch_intensity.attr); |
| if (err < 0) { |
| pr_err("Failed to register force touch intensity sysfs : %d\n", err); |
| goto err_timed_output_register; |
| } |
| |
| /* create sysfs */ |
| zh915data->motor_dev = sec_device_create(zh915data, "motor"); |
| if (IS_ERR(zh915data->motor_dev)) { |
| err = -ENODEV; |
| pr_err("[VIB] Failed to create device\ |
| for samsung specific motor, err num: %d\n", err); |
| goto err_timed_output_register; |
| } |
| err = sysfs_create_group(&zh915data->motor_dev->kobj, &sec_motor_attr_group); |
| if (err) { |
| err = -ENODEV; |
| pr_err("[VIB] Failed to create sysfs group\ |
| for samsung specific motor, err num: %d\n", err); |
| goto exit_sysfs; |
| } |
| |
| i2c_set_clientdata(client,zh915data); |
| |
| zh915_reg_init(zh915data); |
| |
| g_drvdata= zh915data; |
| dev_info(zh915data->dev, |
| "zh915 probe succeeded\n"); |
| gpio_direction_output(zh915data->boost_en, 0); |
| |
| return 0; |
| |
| exit_sysfs: |
| sec_device_destroy(zh915data->motor_dev->devt); |
| err_timed_output_register: |
| sysfs_remove_group(&zh915data->motor_dev->kobj, &sec_motor_attr_group); |
| exit_sec_devices: |
| dev_err(zh915data->dev, |
| "%s failed, err=%d\n", |
| __FUNCTION__, err); |
| kfree(zh915data->multi_duty); |
| kfree(zh915data->multi_freq); |
| exit_probe: |
| return err; |
| } |
| |
| static int zh915_remove(struct i2c_client* client) |
| { |
| return 0; |
| } |
| |
| extern int haptic_homekey_press(void) |
| { |
| struct hrtimer *timer; |
| struct zh915_data *zh915data; |
| int loop_cnt = 5; |
| |
| if(g_drvdata == NULL) |
| return -1; |
| timer = &g_drvdata->timer; |
| |
| zh915data = g_drvdata; |
| |
| // mot_boost_on |
| gpio_set_value(zh915data->boost_en, 1); |
| while (!gpio_get_value(zh915data->boost_en) || !loop_cnt) |
| { |
| mdelay(1); |
| loop_cnt--; |
| } |
| mdelay(2); |
| zh915_reg_write(zh915data, REG_CONTROL, CONTROL_LOOP_MASK); // LPA, LOOP OPEN |
| |
| mutex_lock(&zh915data->lock); |
| zh915data->timeout = HOMEKEY_DURATION; |
| zh915_haptic_set_frequency(zh915data, HOMEKEY_PRESS_FREQ); |
| if (zh915data->force_touch_intensity == -1) |
| zh915_haptic_set_intensity(zh915data, MAX_INTENSITY); |
| else |
| zh915_haptic_set_intensity(zh915data, zh915data->force_touch_intensity); |
| // vibrator enable |
| zh915_reg_write(zh915data, REG_RESONANCE_FREQ, zh915data->freq); // Frequency set |
| zh915_reg_write(zh915data, REG_STRENGTH_WRITE, zh915data->duty); // strength set |
| zh915_reg_write(zh915data, REG_MODE, I2C_MODE & MODE_MASK); // motor enable |
| |
| pr_info("[VIB] %s homekey press freq:%d, intensity:%d, time:%d\n", __func__, HOMEKEY_PRESS_FREQ, zh915data->force_touch_intensity, zh915data->timeout); |
| zh915data->running = true; |
| mutex_unlock(&zh915data->lock); |
| |
| hrtimer_start(timer, ns_to_ktime((u64)zh915data->timeout * NSEC_PER_MSEC), |
| HRTIMER_MODE_REL); |
| |
| return 0; |
| } |
| |
| extern int haptic_homekey_release(void) |
| { |
| struct hrtimer *timer; |
| struct zh915_data *zh915data; |
| int loop_cnt = 5; |
| |
| if(g_drvdata == NULL) |
| return -1; |
| zh915data = g_drvdata; |
| timer = &zh915data->timer; |
| |
| // mot_boost_on |
| if (!gpio_get_value(zh915data->boost_en)) { |
| gpio_set_value(zh915data->boost_en, 1); |
| while (!gpio_get_value(zh915data->boost_en) || !loop_cnt) |
| { |
| mdelay(1); |
| loop_cnt--; |
| } |
| mdelay(2); |
| } |
| zh915_reg_write(zh915data, REG_CONTROL, CONTROL_LOOP_MASK); // LPA, LOOP OPEN |
| |
| mutex_lock(&zh915data->lock); |
| zh915data->timeout = HOMEKEY_DURATION; |
| zh915_haptic_set_frequency(zh915data, HOMEKEY_RELEASE_FREQ); |
| if (zh915data->force_touch_intensity == -1) |
| zh915_haptic_set_intensity(zh915data, MAX_INTENSITY); |
| else |
| zh915_haptic_set_intensity(zh915data, zh915data->force_touch_intensity); |
| |
| zh915_reg_write(zh915data, REG_RESONANCE_FREQ, zh915data->freq); // Frequency set |
| zh915_reg_write(zh915data, REG_STRENGTH_WRITE, zh915data->duty); // strength set |
| zh915_reg_write(zh915data, REG_MODE, I2C_MODE & MODE_MASK); // motor enable |
| |
| pr_info("%s homekey release freq:%d, intensity:%d, time:%d\n", __func__, HOMEKEY_RELEASE_FREQ, zh915data->force_touch_intensity, zh915data->timeout); |
| zh915data->running = true; |
| mutex_unlock(&zh915data->lock); |
| |
| hrtimer_start(timer, ns_to_ktime((u64)zh915data->timeout * NSEC_PER_MSEC), |
| HRTIMER_MODE_REL); |
| |
| return 0; |
| } |
| |
| static struct i2c_device_id zh915_id_table[] = |
| { |
| { HAPTICS_DEVICE_NAME, 0 }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(i2c, zh915_id_table); |
| |
| #ifdef CONFIG_OF |
| static struct of_device_id zh915_dt_ids[] = |
| { |
| { .compatible = "zh915" }, |
| { }, |
| }; |
| MODULE_DEVICE_TABLE(of, zh915_dt_ids); |
| #endif |
| |
| static struct i2c_driver zh915_driver = |
| { |
| .driver = { |
| .name = HAPTICS_DEVICE_NAME, |
| .owner = THIS_MODULE, |
| #ifdef CONFIG_OF |
| .of_match_table = zh915_dt_ids, |
| #endif |
| }, |
| .id_table = zh915_id_table, |
| .probe = zh915_probe, |
| .remove = zh915_remove, |
| }; |
| |
| static int __init zh915_init(void) |
| { |
| pr_info("[VIB] %s\n", __func__); |
| return i2c_add_driver(&zh915_driver); |
| } |
| |
| static void __exit zh915_exit(void) |
| { |
| i2c_del_driver(&zh915_driver); |
| } |
| |
| module_init(zh915_init); |
| module_exit(zh915_exit); |
| |
| MODULE_AUTHOR("jwon88.jung@samsung.com"); |
| MODULE_DESCRIPTION("Driver for zh925"); |