/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../staging/android/timed_output.h" #define QPNP_IRQ_FLAGS (IRQF_TRIGGER_RISING | \ IRQF_TRIGGER_FALLING | \ IRQF_ONESHOT) #define QPNP_HAP_STATUS(b) (b + 0x0A) #define QPNP_HAP_LRA_AUTO_RES_LO(b) (b + 0x0B) #define QPNP_HAP_LRA_AUTO_RES_HI(b) (b + 0x0C) #define QPNP_HAP_EN_CTL_REG(b) (b + 0x46) #define QPNP_HAP_EN_CTL2_REG(b) (b + 0x48) #define QPNP_HAP_ACT_TYPE_REG(b) (b + 0x4C) #define QPNP_HAP_WAV_SHAPE_REG(b) (b + 0x4D) #define QPNP_HAP_PLAY_MODE_REG(b) (b + 0x4E) #define QPNP_HAP_LRA_AUTO_RES_REG(b) (b + 0x4F) #define QPNP_HAP_VMAX_REG(b) (b + 0x51) #define QPNP_HAP_ILIM_REG(b) (b + 0x52) #define QPNP_HAP_SC_DEB_REG(b) (b + 0x53) #define QPNP_HAP_RATE_CFG1_REG(b) (b + 0x54) #define QPNP_HAP_RATE_CFG2_REG(b) (b + 0x55) #define QPNP_HAP_INT_PWM_REG(b) (b + 0x56) #define QPNP_HAP_EXT_PWM_REG(b) (b + 0x57) #define QPNP_HAP_PWM_CAP_REG(b) (b + 0x58) #define QPNP_HAP_SC_CLR_REG(b) (b + 0x59) #define QPNP_HAP_SC_IRQ_STATUS_DELAY msecs_to_jiffies(1000) #define QPNP_HAP_BRAKE_REG(b) (b + 0x5C) #define QPNP_HAP_WAV_REP_REG(b) (b + 0x5E) #define QPNP_HAP_WAV_S_REG_BASE(b) (b + 0x60) #define QPNP_HAP_PLAY_REG(b) (b + 0x70) #define QPNP_HAP_SEC_ACCESS_REG(b) (b + 0xD0) #define QPNP_HAP_TEST2_REG(b) (b + 0xE3) #define QPNP_HAP_STATUS_BUSY 0x02 #define QPNP_HAP_ACT_TYPE_MASK 0xFE #define QPNP_HAP_LRA 0x0 #define QPNP_HAP_ERM 0x1 #define QPNP_HAP_AUTO_RES_MODE_MASK 0x8F #define QPNP_HAP_AUTO_RES_MODE_SHIFT 4 #define QPNP_HAP_LRA_HIGH_Z_MASK 0xF3 #define QPNP_HAP_LRA_HIGH_Z_SHIFT 2 #define QPNP_HAP_LRA_RES_CAL_PER_MASK 0xFC #define QPNP_HAP_RES_CAL_PERIOD_MIN 4 #define QPNP_HAP_RES_CAL_PERIOD_MAX 32 #define QPNP_HAP_PLAY_MODE_MASK 0xCF #define QPNP_HAP_PLAY_MODE_SHFT 4 #define QPNP_HAP_VMAX_MASK 0xC1 #define QPNP_HAP_VMAX_SHIFT 1 #define QPNP_HAP_VMAX_MIN_MV 116 #define QPNP_HAP_VMAX_MAX_MV 3596 #define QPNP_HAP_ILIM_MASK 0xFE #define QPNP_HAP_ILIM_MIN_MV 400 #define QPNP_HAP_ILIM_MAX_MV 800 #define QPNP_HAP_SC_DEB_MASK 0xF8 #define QPNP_HAP_SC_DEB_SUB 2 #define QPNP_HAP_SC_DEB_CYCLES_MIN 0 #define QPNP_HAP_DEF_SC_DEB_CYCLES 8 #define QPNP_HAP_SC_DEB_CYCLES_MAX 32 #define QPNP_HAP_SC_CLR 1 #define QPNP_HAP_INT_PWM_MASK 0xFC #define QPNP_HAP_INT_PWM_FREQ_253_KHZ 253 #define QPNP_HAP_INT_PWM_FREQ_505_KHZ 505 #define QPNP_HAP_INT_PWM_FREQ_739_KHZ 739 #define QPNP_HAP_INT_PWM_FREQ_1076_KHZ 1076 #define QPNP_HAP_WAV_SHAPE_MASK 0xFE #define QPNP_HAP_RATE_CFG1_MASK 0xFF #define QPNP_HAP_RATE_CFG2_MASK 0xF0 #define QPNP_HAP_RATE_CFG2_SHFT 8 #define QPNP_HAP_RATE_CFG_STEP_US 5 #define QPNP_HAP_WAV_PLAY_RATE_US_MIN 0 #define QPNP_HAP_DEF_WAVE_PLAY_RATE_US 5715 #define QPNP_HAP_WAV_PLAY_RATE_US_MAX 20475 #define QPNP_HAP_WAV_REP_MASK 0x8F #define QPNP_HAP_WAV_S_REP_MASK 0xFC #define QPNP_HAP_WAV_REP_SHFT 4 #define QPNP_HAP_WAV_REP_MIN 1 #define QPNP_HAP_WAV_REP_MAX 128 #define QPNP_HAP_WAV_S_REP_MIN 1 #define QPNP_HAP_WAV_S_REP_MAX 8 #define QPNP_HAP_BRAKE_PAT_MASK 0x3 #define QPNP_HAP_ILIM_MIN_MA 400 #define QPNP_HAP_ILIM_MAX_MA 800 #define QPNP_HAP_EXT_PWM_MASK 0xFC #define QPNP_HAP_EXT_PWM_FREQ_25_KHZ 25 #define QPNP_HAP_EXT_PWM_FREQ_50_KHZ 50 #define QPNP_HAP_EXT_PWM_FREQ_75_KHZ 75 #define QPNP_HAP_EXT_PWM_FREQ_100_KHZ 100 #define PWM_MAX_DTEST_LINES 4 #define QPNP_HAP_EXT_PWM_DTEST_MASK 0x0F #define QPNP_HAP_EXT_PWM_DTEST_SHFT 4 #define QPNP_HAP_EXT_PWM_PEAK_DATA 0x7F #define QPNP_HAP_EXT_PWM_HALF_DUTY 50 #define QPNP_HAP_EXT_PWM_FULL_DUTY 100 #define QPNP_HAP_EXT_PWM_DATA_FACTOR 39 #define QPNP_HAP_WAV_SINE 0 #define QPNP_HAP_WAV_SQUARE 1 #define QPNP_HAP_WAV_SAMP_LEN 8 #define QPNP_HAP_WAV_SAMP_MAX 0x7E #define QPNP_HAP_BRAKE_PAT_LEN 4 #define QPNP_HAP_PLAY_EN 0x80 #define QPNP_HAP_EN 0x80 #define QPNP_HAP_BRAKE_MASK 0xFE #define QPNP_HAP_TEST2_AUTO_RES_MASK 0x7F #define QPNP_HAP_SEC_UNLOCK 0xA5 #define AUTO_RES_ENABLE 0x80 #define AUTO_RES_DISABLE 0x00 #define AUTO_RES_ERR_BIT 0x10 #define SC_FOUND_BIT 0x08 #define SC_MAX_DURATION 5 #define QPNP_HAP_TIMEOUT_MS_MAX 15000 #define QPNP_HAP_STR_SIZE 20 #define QPNP_HAP_MAX_RETRIES 5 #define QPNP_HAP_CYCLS 5 #define QPNP_TEST_TIMER_MS 5 #define AUTO_RES_ENABLE_TIMEOUT 20000 #define AUTO_RES_ERR_CAPTURE_RES 5 #define AUTO_RES_ERR_MAX 15 #define MISC_TRIM_ERROR_RC19P2_CLK 0x09F5 #define MISC_SEC_ACCESS 0x09D0 #define MISC_SEC_UNLOCK 0xA5 #define PMI8950_MISC_SID 2 #define POLL_TIME_AUTO_RES_ERR_NS (5 * NSEC_PER_MSEC) #define LRA_POS_FREQ_COUNT 6 int lra_play_rate_code[LRA_POS_FREQ_COUNT]; /* haptic debug register set */ static u8 qpnp_hap_dbg_regs[] = { 0x0a, 0x0b, 0x0c, 0x46, 0x48, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x5c, 0x5e, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x70, 0xE3, }; /* ramp up/down test sequence */ static u8 qpnp_hap_ramp_test_data[] = { 0x0, 0x19, 0x32, 0x4C, 0x65, 0x7F, 0x65, 0x4C, 0x32, 0x19, 0x0, 0x99, 0xB2, 0xCC, 0xE5, 0xFF, 0xE5, 0xCC, 0xB2, 0x99, 0x0, 0x19, 0x32, 0x4C, 0x65, 0x7F, 0x65, 0x4C, 0x32, 0x19, 0x0, 0x99, 0xB2, 0xCC, 0xE5, 0xFF, 0xE5, 0xCC, 0xB2, 0x99, 0x0, 0x19, 0x32, 0x4C, 0x65, 0x7F, 0x65, 0x4C, 0x32, 0x19, 0x0, 0x99, 0xB2, 0xCC, 0xE5, 0xFF, 0xE5, 0xCC, 0xB2, 0x99, 0x0, 0x19, 0x32, 0x4C, 0x65, 0x7F, 0x65, 0x4C, 0x32, 0x19, 0x0, 0x99, 0xB2, 0xCC, 0xE5, 0xFF, 0xE5, 0xCC, 0xB2, 0x99, 0x0, 0x19, 0x32, 0x4C, 0x65, 0x7F, 0x65, 0x4C, 0x32, 0x19, 0x0, 0x99, 0xB2, 0xCC, 0xE5, 0xFF, 0xE5, 0xCC, 0xB2, 0x99, 0x0, 0x19, 0x32, 0x4C, 0x65, 0x7F, 0x65, 0x4C, 0x32, 0x19, 0x0, 0x99, 0xB2, 0xCC, 0xE5, 0xFF, 0xE5, 0xCC, 0xB2, 0x99, 0x0, 0x19, 0x32, 0x4C, 0x65, 0x7F, 0x65, 0x4C, 0x32, 0x19, 0x0, 0x99, 0xB2, 0xCC, 0xE5, 0xFF, 0xE5, 0xCC, 0xB2, 0x99, }; /* alternate max and min sequence */ static u8 qpnp_hap_min_max_test_data[] = { 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, 0x0, 0x7F, 0x0, 0xFF, }; /* * auto resonance mode * ZXD - Zero Cross Detect * QWD - Quarter Wave Drive * ZXD_EOP - ZXD with End Of Pattern */ enum qpnp_hap_auto_res_mode { QPNP_HAP_AUTO_RES_NONE, QPNP_HAP_AUTO_RES_ZXD, QPNP_HAP_AUTO_RES_QWD, QPNP_HAP_AUTO_RES_MAX_QWD, QPNP_HAP_AUTO_RES_ZXD_EOP, }; /* high Z option lines */ enum qpnp_hap_high_z { QPNP_HAP_LRA_HIGH_Z_NONE, QPNP_HAP_LRA_HIGH_Z_OPT1, QPNP_HAP_LRA_HIGH_Z_OPT2, QPNP_HAP_LRA_HIGH_Z_OPT3, }; /* play modes */ enum qpnp_hap_mode { QPNP_HAP_DIRECT, QPNP_HAP_BUFFER, QPNP_HAP_AUDIO, QPNP_HAP_PWM, }; /* pwm channel info */ struct qpnp_pwm_info { struct pwm_device *pwm_dev; u32 pwm_channel; u32 duty_us; u32 period_us; }; /* * qpnp_hap - Haptic data structure * @ spmi - spmi device * @ hap_timer - hrtimer * @ auto_res_err_poll_timer - hrtimer for auto-resonance error * @ timed_dev - timed output device * @ work - worker * @ auto_res_err_work - correct auto resonance error * @ sc_work - worker to handle short circuit condition * @ pwm_info - pwm info * @ lock - mutex lock * @ wf_lock - mutex lock for waveform * @ play_mode - play mode * @ auto_res_mode - auto resonace mode * @ lra_high_z - high z option line * @ timeout_ms - max timeout in ms * @ vmax_mv - max voltage in mv * @ ilim_ma - limiting current in ma * @ sc_deb_cycles - short circuit debounce cycles * @ int_pwm_freq_khz - internal pwm frequency in khz * @ wave_play_rate_us - play rate for waveform * @ ext_pwm_freq_khz - external pwm frequency in khz * @ wave_rep_cnt - waveform repeat count * @ wave_s_rep_cnt - waveform sample repeat count * @ play_irq - irq for play * @ sc_irq - irq for short circuit * @ base - base address * @ act_type - actuator type * @ wave_shape - waveform shape * @ wave_samp - array of wave samples * @ shadow_wave_samp - shadow array of wave samples * @ brake_pat - pattern for active breaking * @ reg_en_ctl - enable control register * @ reg_play - play register * @ lra_res_cal_period - period for resonance calibration * @ sc_duration - counter to determine the duration of short circuit condition * @ state - current state of haptics * @ use_play_irq - play irq usage state * @ use_sc_irq - short circuit irq usage state * @ wf_update - waveform update flag * @ pwm_cfg_state - pwm mode configuration state * @ buffer_cfg_state - buffer mode configuration state * @ en_brake - brake state * @ sup_brake_pat - support custom brake pattern * @ correct_lra_drive_freq - correct LRA Drive Frequency * @ misc_trim_error_rc19p2_clk_reg_present - if MISC Trim Error reg is present */ struct qpnp_hap { struct spmi_device *spmi; struct regulator *vcc_pon; struct hrtimer hap_timer; struct hrtimer auto_res_err_poll_timer; struct timed_output_dev timed_dev; struct work_struct work; struct work_struct auto_res_err_work; struct delayed_work sc_work; struct hrtimer hap_test_timer; struct work_struct test_work; struct qpnp_pwm_info pwm_info; struct mutex lock; struct mutex wf_lock; struct completion completion; enum qpnp_hap_mode play_mode; enum qpnp_hap_auto_res_mode auto_res_mode; enum qpnp_hap_high_z lra_high_z; u32 timeout_ms; u32 vmax_mv; u32 ilim_ma; u32 sc_deb_cycles; u32 int_pwm_freq_khz; u32 wave_play_rate_us; u32 ext_pwm_freq_khz; u32 wave_rep_cnt; u32 wave_s_rep_cnt; u32 play_irq; u32 sc_irq; u16 base; u8 act_type; u8 wave_shape; u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN]; u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN]; u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN]; u8 reg_en_ctl; u8 reg_play; u8 lra_res_cal_period; u8 sc_duration; u8 ext_pwm_dtest_line; bool state; bool use_play_irq; bool use_sc_irq; bool manage_pon_supply; bool wf_update; bool pwm_cfg_state; bool buffer_cfg_state; bool en_brake; bool sup_brake_pat; bool correct_lra_drive_freq; bool misc_trim_error_rc19p2_clk_reg_present; }; static struct qpnp_hap *ghap; /* helper to read a pmic register */ static int qpnp_hap_read_reg(struct qpnp_hap *hap, u8 *data, u16 addr) { int rc; rc = spmi_ext_register_readl(hap->spmi->ctrl, hap->spmi->sid, addr, data, 1); if (rc < 0) dev_err(&hap->spmi->dev, "Error reading address: %X - ret %X\n", addr, rc); return rc; } /* helper to write a pmic register */ static int qpnp_hap_write_reg(struct qpnp_hap *hap, u8 *data, u16 addr) { int rc; rc = spmi_ext_register_writel(hap->spmi->ctrl, hap->spmi->sid, addr, data, 1); if (rc < 0) dev_err(&hap->spmi->dev, "Error writing address: %X - ret %X\n", addr, rc); dev_dbg(&hap->spmi->dev, "write: HAP_0x%x = 0x%x\n", addr, *data); return rc; } /* helper to access secure registers */ static int qpnp_hap_sec_access(struct qpnp_hap *hap) { int rc; u8 reg = QPNP_HAP_SEC_UNLOCK; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_SEC_ACCESS_REG(hap->base)); if (rc) return rc; return 0; } static void qpnp_handle_sc_irq(struct work_struct *work) { struct qpnp_hap *hap = container_of(work, struct qpnp_hap, sc_work.work); u8 val, reg; qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); /* clear short circuit register */ if (val & SC_FOUND_BIT) { hap->sc_duration++; reg = QPNP_HAP_SC_CLR; qpnp_hap_write_reg(hap, ®, QPNP_HAP_SC_CLR_REG(hap->base)); } } static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on) { u8 val; int rc, i; val = hap->reg_en_ctl; if (on) { val |= QPNP_HAP_EN; } else { for (i = 0; i < QPNP_HAP_MAX_RETRIES; i++) { /* wait for 4 cycles of play rate */ unsigned long sleep_time = QPNP_HAP_CYCLS * hap->wave_play_rate_us; rc = qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); dev_dbg(&hap->spmi->dev, "HAP_STATUS=0x%x\n", val); /* wait for QPNP_HAP_CYCLS cycles of play rate */ if (val & QPNP_HAP_STATUS_BUSY) { usleep_range(sleep_time, sleep_time + 1); if (hap->play_mode == QPNP_HAP_DIRECT || hap->play_mode == QPNP_HAP_PWM) break; } else break; } if (i >= QPNP_HAP_MAX_RETRIES) dev_dbg(&hap->spmi->dev, "Haptics Busy. Force disable\n"); val &= ~QPNP_HAP_EN; } rc = qpnp_hap_write_reg(hap, &val, QPNP_HAP_EN_CTL_REG(hap->base)); if (rc < 0) return rc; hap->reg_en_ctl = val; return 0; } static int qpnp_hap_play(struct qpnp_hap *hap, int on) { u8 val; int rc; val = hap->reg_play; if (on) val |= QPNP_HAP_PLAY_EN; else val &= ~QPNP_HAP_PLAY_EN; rc = qpnp_hap_write_reg(hap, &val, QPNP_HAP_PLAY_REG(hap->base)); if (rc < 0) return rc; hap->reg_play = val; return 0; } /* sysfs show debug registers */ static ssize_t qpnp_hap_dump_regs_show(struct device *dev, struct device_attribute *attr, char *buf) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); int count = 0, i; u8 val; for (i = 0; i < ARRAY_SIZE(qpnp_hap_dbg_regs); i++) { qpnp_hap_read_reg(hap, &val, hap->base + qpnp_hap_dbg_regs[i]); count += snprintf(buf + count, PAGE_SIZE - count, "qpnp_haptics: REG_0x%x = 0x%x\n", hap->base + qpnp_hap_dbg_regs[i], val); if (count >= PAGE_SIZE) return PAGE_SIZE - 1; } return count; } /* play irq handler */ static irqreturn_t qpnp_hap_play_irq(int irq, void *_hap) { struct qpnp_hap *hap = _hap; int i, rc; u8 reg; mutex_lock(&hap->wf_lock); /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */ for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN && hap->wf_update; i++) { reg = hap->wave_samp[i] = hap->shadow_wave_samp[i]; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_WAV_S_REG_BASE(hap->base) + i); if (rc) goto unlock; } hap->wf_update = false; unlock: mutex_unlock(&hap->wf_lock); return IRQ_HANDLED; } /* short circuit irq handler */ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap) { struct qpnp_hap *hap = _hap; int rc; u8 disable_haptics = 0x00; u8 val; dev_dbg(&hap->spmi->dev, "Short circuit detected\n"); if (hap->sc_duration < SC_MAX_DURATION) { qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); if (val & SC_FOUND_BIT) schedule_delayed_work(&hap->sc_work, QPNP_HAP_SC_IRQ_STATUS_DELAY); else hap->sc_duration = 0; } else { /* Disable haptics module if the duration of short circuit * exceeds the maximum limit (5 secs). */ rc = qpnp_hap_write_reg(hap, &disable_haptics, QPNP_HAP_EN_CTL_REG(hap->base)); dev_err(&hap->spmi->dev, "Haptics disabled permanently due to short circuit\n"); } return IRQ_HANDLED; } /* configuration api for buffer mode */ static int qpnp_hap_buffer_config(struct qpnp_hap *hap) { u8 reg = 0; int rc, i, temp; /* Configure the WAVE_REPEAT register */ if (hap->wave_rep_cnt < QPNP_HAP_WAV_REP_MIN) hap->wave_rep_cnt = QPNP_HAP_WAV_REP_MIN; else if (hap->wave_rep_cnt > QPNP_HAP_WAV_REP_MAX) hap->wave_rep_cnt = QPNP_HAP_WAV_REP_MAX; if (hap->wave_s_rep_cnt < QPNP_HAP_WAV_S_REP_MIN) hap->wave_s_rep_cnt = QPNP_HAP_WAV_S_REP_MIN; else if (hap->wave_s_rep_cnt > QPNP_HAP_WAV_S_REP_MAX) hap->wave_s_rep_cnt = QPNP_HAP_WAV_S_REP_MAX; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_WAV_REP_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_WAV_REP_MASK; temp = fls(hap->wave_rep_cnt) - 1; reg |= (temp << QPNP_HAP_WAV_REP_SHFT); reg &= QPNP_HAP_WAV_S_REP_MASK; temp = fls(hap->wave_s_rep_cnt) - 1; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_WAV_REP_REG(hap->base)); if (rc) return rc; /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */ for (i = 0, reg = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) { reg = hap->wave_samp[i]; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_WAV_S_REG_BASE(hap->base) + i); if (rc) return rc; } /* setup play irq */ if (hap->use_play_irq) { rc = devm_request_threaded_irq(&hap->spmi->dev, hap->play_irq, NULL, qpnp_hap_play_irq, QPNP_IRQ_FLAGS, "qpnp_play_irq", hap); if (rc < 0) { dev_err(&hap->spmi->dev, "Unable to request play(%d) IRQ(err:%d)\n", hap->play_irq, rc); return rc; } } hap->buffer_cfg_state = true; return 0; } /* configuration api for pwm */ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) { u8 reg = 0; int rc, temp; /* Configure the EXTERNAL_PWM register */ if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_25_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_25_KHZ; temp = 0; } else if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_50_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_50_KHZ; temp = 1; } else if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_75_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_75_KHZ; temp = 2; } else { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_100_KHZ; temp = 3; } rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EXT_PWM_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_EXT_PWM_MASK; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_EXT_PWM_REG(hap->base)); if (rc) return rc; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_TEST2_REG(hap->base)); if (rc) return rc; if (!hap->ext_pwm_dtest_line || hap->ext_pwm_dtest_line > PWM_MAX_DTEST_LINES) { dev_err(&hap->spmi->dev, "invalid dtest line\n"); return -EINVAL; } /* disable auto res for PWM mode */ reg &= QPNP_HAP_EXT_PWM_DTEST_MASK; temp = hap->ext_pwm_dtest_line << QPNP_HAP_EXT_PWM_DTEST_SHFT; reg |= temp; /* TEST2 is a secure access register */ rc = qpnp_hap_sec_access(hap); if (rc) return rc; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_TEST2_REG(hap->base)); if (rc) return rc; rc = pwm_config(hap->pwm_info.pwm_dev, hap->pwm_info.duty_us * NSEC_PER_USEC, hap->pwm_info.period_us * NSEC_PER_USEC); if (rc < 0) { dev_err(&hap->spmi->dev, "hap pwm config failed\n"); pwm_free(hap->pwm_info.pwm_dev); return -ENODEV; } hap->pwm_cfg_state = true; return 0; } /* configuration api for play mode */ static int qpnp_hap_play_mode_config(struct qpnp_hap *hap) { u8 reg = 0; int rc, temp; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PLAY_MODE_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_PLAY_MODE_MASK; temp = hap->play_mode << QPNP_HAP_PLAY_MODE_SHFT; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_PLAY_MODE_REG(hap->base)); if (rc) return rc; return 0; } /* configuration api for max volatge */ static int qpnp_hap_vmax_config(struct qpnp_hap *hap) { u8 reg = 0; int rc, temp; if (hap->vmax_mv < QPNP_HAP_VMAX_MIN_MV) hap->vmax_mv = QPNP_HAP_VMAX_MIN_MV; else if (hap->vmax_mv > QPNP_HAP_VMAX_MAX_MV) hap->vmax_mv = QPNP_HAP_VMAX_MAX_MV; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_VMAX_MASK; temp = hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV; reg |= (temp << QPNP_HAP_VMAX_SHIFT); rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); if (rc) return rc; return 0; } /* configuration api for short circuit debounce */ static int qpnp_hap_sc_deb_config(struct qpnp_hap *hap) { u8 reg = 0; int rc, temp; if (hap->sc_deb_cycles < QPNP_HAP_SC_DEB_CYCLES_MIN) hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MIN; else if (hap->sc_deb_cycles > QPNP_HAP_SC_DEB_CYCLES_MAX) hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MAX; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_SC_DEB_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_SC_DEB_MASK; if (hap->sc_deb_cycles) { temp = fls(hap->sc_deb_cycles) - 1; reg |= temp - QPNP_HAP_SC_DEB_SUB; } rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_SC_DEB_REG(hap->base)); if (rc) return rc; return 0; } /* DT parsing api for buffer mode */ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap) { struct spmi_device *spmi = hap->spmi; struct property *prop; u32 temp; int rc, i; hap->wave_rep_cnt = QPNP_HAP_WAV_REP_MIN; rc = of_property_read_u32(spmi->dev.of_node, "qcom,wave-rep-cnt", &temp); if (!rc) { hap->wave_rep_cnt = temp; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read rep cnt\n"); return rc; } hap->wave_s_rep_cnt = QPNP_HAP_WAV_S_REP_MIN; rc = of_property_read_u32(spmi->dev.of_node, "qcom,wave-samp-rep-cnt", &temp); if (!rc) { hap->wave_s_rep_cnt = temp; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read samp rep cnt\n"); return rc; } prop = of_find_property(spmi->dev.of_node, "qcom,wave-samples", &temp); if (!prop || temp != QPNP_HAP_WAV_SAMP_LEN) { dev_err(&spmi->dev, "Invalid wave samples, use default"); for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) hap->wave_samp[i] = QPNP_HAP_WAV_SAMP_MAX; } else { memcpy(hap->wave_samp, prop->value, QPNP_HAP_WAV_SAMP_LEN); } hap->use_play_irq = of_property_read_bool(spmi->dev.of_node, "qcom,use-play-irq"); if (hap->use_play_irq) { hap->play_irq = spmi_get_irq_byname(hap->spmi, NULL, "play-irq"); if (hap->play_irq < 0) { dev_err(&spmi->dev, "Unable to get play irq\n"); return hap->play_irq; } } return 0; } /* DT parsing api for PWM mode */ static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap) { struct spmi_device *spmi = hap->spmi; u32 temp; int rc; hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_25_KHZ; rc = of_property_read_u32(spmi->dev.of_node, "qcom,ext-pwm-freq-khz", &temp); if (!rc) { hap->ext_pwm_freq_khz = temp; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read ext pwm freq\n"); return rc; } rc = of_property_read_u32(spmi->dev.of_node, "qcom,pwm-channel", &temp); if (!rc) hap->pwm_info.pwm_channel = temp; else return rc; hap->pwm_info.pwm_dev = of_pwm_get(spmi->dev.of_node, NULL); if (IS_ERR(hap->pwm_info.pwm_dev)) { rc = PTR_ERR(hap->pwm_info.pwm_dev); dev_err(&spmi->dev, "Cannot get PWM device rc:(%d)\n", rc); hap->pwm_info.pwm_dev = NULL; return rc; } rc = of_property_read_u32(spmi->dev.of_node, "qcom,period-us", &temp); if (!rc) hap->pwm_info.period_us = temp; else return rc; rc = of_property_read_u32(spmi->dev.of_node, "qcom,duty-us", &temp); if (!rc) hap->pwm_info.duty_us = temp; else return rc; rc = of_property_read_u32(spmi->dev.of_node, "qcom,ext-pwm-dtest-line", &temp); if (!rc) hap->ext_pwm_dtest_line = temp; else return rc; return 0; } /* sysfs show for wave samples */ static ssize_t qpnp_hap_wf_samp_show(struct device *dev, char *buf, int index) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) { dev_err(dev, "Invalid sample index(%d)\n", index); return -EINVAL; } return snprintf(buf, PAGE_SIZE, "0x%x\n", hap->shadow_wave_samp[index]); } static ssize_t qpnp_hap_wf_s0_show(struct device *dev, struct device_attribute *attr, char *buf) { return qpnp_hap_wf_samp_show(dev, buf, 0); } static ssize_t qpnp_hap_wf_s1_show(struct device *dev, struct device_attribute *attr, char *buf) { return qpnp_hap_wf_samp_show(dev, buf, 1); } static ssize_t qpnp_hap_wf_s2_show(struct device *dev, struct device_attribute *attr, char *buf) { return qpnp_hap_wf_samp_show(dev, buf, 2); } static ssize_t qpnp_hap_wf_s3_show(struct device *dev, struct device_attribute *attr, char *buf) { return qpnp_hap_wf_samp_show(dev, buf, 3); } static ssize_t qpnp_hap_wf_s4_show(struct device *dev, struct device_attribute *attr, char *buf) { return qpnp_hap_wf_samp_show(dev, buf, 4); } static ssize_t qpnp_hap_wf_s5_show(struct device *dev, struct device_attribute *attr, char *buf) { return qpnp_hap_wf_samp_show(dev, buf, 5); } static ssize_t qpnp_hap_wf_s6_show(struct device *dev, struct device_attribute *attr, char *buf) { return qpnp_hap_wf_samp_show(dev, buf, 6); } static ssize_t qpnp_hap_wf_s7_show(struct device *dev, struct device_attribute *attr, char *buf) { return qpnp_hap_wf_samp_show(dev, buf, 7); } /* sysfs store for wave samples */ static ssize_t qpnp_hap_wf_samp_store(struct device *dev, const char *buf, size_t count, int index) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); int data; if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) { dev_err(dev, "Invalid sample index(%d)\n", index); return -EINVAL; } if (sscanf(buf, "%x", &data) != 1) return -EINVAL; if (data < 0 || data > 0xff) { dev_err(dev, "Invalid sample wf_%d (%d)\n", index, data); return -EINVAL; } hap->shadow_wave_samp[index] = (u8) data; return count; } static ssize_t qpnp_hap_wf_s0_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return qpnp_hap_wf_samp_store(dev, buf, count, 0); } static ssize_t qpnp_hap_wf_s1_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return qpnp_hap_wf_samp_store(dev, buf, count, 1); } static ssize_t qpnp_hap_wf_s2_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return qpnp_hap_wf_samp_store(dev, buf, count, 2); } static ssize_t qpnp_hap_wf_s3_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return qpnp_hap_wf_samp_store(dev, buf, count, 3); } static ssize_t qpnp_hap_wf_s4_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return qpnp_hap_wf_samp_store(dev, buf, count, 4); } static ssize_t qpnp_hap_wf_s5_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return qpnp_hap_wf_samp_store(dev, buf, count, 5); } static ssize_t qpnp_hap_wf_s6_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return qpnp_hap_wf_samp_store(dev, buf, count, 6); } static ssize_t qpnp_hap_wf_s7_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return qpnp_hap_wf_samp_store(dev, buf, count, 7); } /* sysfs show for wave form update */ static ssize_t qpnp_hap_wf_update_show(struct device *dev, struct device_attribute *attr, char *buf) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); return snprintf(buf, PAGE_SIZE, "%d\n", hap->wf_update); } /* sysfs store for updating wave samples */ static ssize_t qpnp_hap_wf_update_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); mutex_lock(&hap->wf_lock); hap->wf_update = true; mutex_unlock(&hap->wf_lock); return count; } /* sysfs show for wave repeat */ static ssize_t qpnp_hap_wf_rep_show(struct device *dev, struct device_attribute *attr, char *buf) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); return snprintf(buf, PAGE_SIZE, "%d\n", hap->wave_rep_cnt); } /* sysfs store for wave repeat */ static ssize_t qpnp_hap_wf_rep_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); int data, rc, temp; u8 reg; if (sscanf(buf, "%d", &data) != 1) return -EINVAL; if (data < QPNP_HAP_WAV_REP_MIN) data = QPNP_HAP_WAV_REP_MIN; else if (data > QPNP_HAP_WAV_REP_MAX) data = QPNP_HAP_WAV_REP_MAX; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_WAV_REP_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_WAV_REP_MASK; temp = fls(data) - 1; reg |= (temp << QPNP_HAP_WAV_REP_SHFT); rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_WAV_REP_REG(hap->base)); if (rc) return rc; hap->wave_rep_cnt = data; return count; } /* sysfs show for wave samples repeat */ static ssize_t qpnp_hap_wf_s_rep_show(struct device *dev, struct device_attribute *attr, char *buf) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); return snprintf(buf, PAGE_SIZE, "%d\n", hap->wave_s_rep_cnt); } /* sysfs store for wave samples repeat */ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); int data, rc, temp; u8 reg; if (sscanf(buf, "%d", &data) != 1) return -EINVAL; if (data < QPNP_HAP_WAV_S_REP_MIN) data = QPNP_HAP_WAV_S_REP_MIN; else if (data > QPNP_HAP_WAV_S_REP_MAX) data = QPNP_HAP_WAV_S_REP_MAX; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_WAV_REP_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_WAV_S_REP_MASK; temp = fls(data) - 1; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_WAV_REP_REG(hap->base)); if (rc) return rc; hap->wave_s_rep_cnt = data; return count; } /* sysfs store function for play mode*/ static ssize_t qpnp_hap_play_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); char str[QPNP_HAP_STR_SIZE + 1]; int rc = 0, temp, old_mode, i; if (snprintf(str, QPNP_HAP_STR_SIZE, "%s", buf) > QPNP_HAP_STR_SIZE) return -EINVAL; for (i = 0; i < strlen(str); i++) { if (str[i] == ' ' || str[i] == '\n' || str[i] == '\t') { str[i] = '\0'; break; } } if (strcmp(str, "buffer") == 0) temp = QPNP_HAP_BUFFER; else if (strcmp(str, "direct") == 0) temp = QPNP_HAP_DIRECT; else if (strcmp(str, "audio") == 0) temp = QPNP_HAP_AUDIO; else if (strcmp(str, "pwm") == 0) temp = QPNP_HAP_PWM; else return -EINVAL; if (temp == hap->play_mode) return count; if (temp == QPNP_HAP_BUFFER && !hap->buffer_cfg_state) { rc = qpnp_hap_parse_buffer_dt(hap); if (!rc) rc = qpnp_hap_buffer_config(hap); } else if (temp == QPNP_HAP_PWM && !hap->pwm_cfg_state) { rc = qpnp_hap_parse_pwm_dt(hap); if (!rc) rc = qpnp_hap_pwm_config(hap); } if (rc < 0) return rc; rc = qpnp_hap_mod_enable(hap, false); if (rc < 0) return rc; old_mode = hap->play_mode; hap->play_mode = temp; /* Configure the PLAY MODE register */ rc = qpnp_hap_play_mode_config(hap); if (rc) { hap->play_mode = old_mode; return rc; } if (hap->play_mode == QPNP_HAP_AUDIO) { rc = qpnp_hap_mod_enable(hap, true); if (rc < 0) { hap->play_mode = old_mode; return rc; } } return count; } /* sysfs show function for play mode */ static ssize_t qpnp_hap_play_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); char *str; if (hap->play_mode == QPNP_HAP_BUFFER) str = "buffer"; else if (hap->play_mode == QPNP_HAP_DIRECT) str = "direct"; else if (hap->play_mode == QPNP_HAP_AUDIO) str = "audio"; else if (hap->play_mode == QPNP_HAP_PWM) str = "pwm"; else return -EINVAL; return snprintf(buf, PAGE_SIZE, "%s\n", str); } /* sysfs store for ramp test data */ static ssize_t qpnp_hap_min_max_test_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); int value = QPNP_TEST_TIMER_MS, i; mutex_lock(&hap->lock); qpnp_hap_mod_enable(hap, true); for (i = 0; i < ARRAY_SIZE(qpnp_hap_min_max_test_data); i++) { hrtimer_start(&hap->hap_test_timer, ktime_set(value / 1000, (value % 1000) * 1000000), HRTIMER_MODE_REL); qpnp_hap_play_byte(qpnp_hap_min_max_test_data[i], true); wait_for_completion(&hap->completion); } qpnp_hap_play_byte(0, false); qpnp_hap_mod_enable(hap, false); mutex_unlock(&hap->lock); return count; } /* sysfs show function for min max test data */ static ssize_t qpnp_hap_min_max_test_data_show(struct device *dev, struct device_attribute *attr, char *buf) { int count = 0, i; for (i = 0; i < ARRAY_SIZE(qpnp_hap_min_max_test_data); i++) { count += snprintf(buf + count, PAGE_SIZE - count, "qpnp_haptics: min_max_test_data[%d] = 0x%x\n", i, qpnp_hap_min_max_test_data[i]); if (count >= PAGE_SIZE) return PAGE_SIZE - 1; } return count; } /* sysfs store for ramp test data */ static ssize_t qpnp_hap_ramp_test_data_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); int value = QPNP_TEST_TIMER_MS, i; mutex_lock(&hap->lock); qpnp_hap_mod_enable(hap, true); for (i = 0; i < ARRAY_SIZE(qpnp_hap_ramp_test_data); i++) { hrtimer_start(&hap->hap_test_timer, ktime_set(value / 1000, (value % 1000) * 1000000), HRTIMER_MODE_REL); qpnp_hap_play_byte(qpnp_hap_ramp_test_data[i], true); wait_for_completion(&hap->completion); } qpnp_hap_play_byte(0, false); qpnp_hap_mod_enable(hap, false); mutex_unlock(&hap->lock); return count; } /* sysfs show function for ramp test data */ static ssize_t qpnp_hap_ramp_test_data_show(struct device *dev, struct device_attribute *attr, char *buf) { int count = 0, i; for (i = 0; i < ARRAY_SIZE(qpnp_hap_ramp_test_data); i++) { count += snprintf(buf + count, PAGE_SIZE - count, "qpnp_haptics: ramp_test_data[%d] = 0x%x\n", i, qpnp_hap_ramp_test_data[i]); if (count >= PAGE_SIZE) return PAGE_SIZE - 1; } return count; } /* sysfs attributes */ static struct device_attribute qpnp_hap_attrs[] = { __ATTR(wf_s0, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_wf_s0_show, qpnp_hap_wf_s0_store), __ATTR(wf_s1, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_wf_s1_show, qpnp_hap_wf_s1_store), __ATTR(wf_s2, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_wf_s2_show, qpnp_hap_wf_s2_store), __ATTR(wf_s3, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_wf_s3_show, qpnp_hap_wf_s3_store), __ATTR(wf_s4, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_wf_s4_show, qpnp_hap_wf_s4_store), __ATTR(wf_s5, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_wf_s5_show, qpnp_hap_wf_s5_store), __ATTR(wf_s6, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_wf_s6_show, qpnp_hap_wf_s6_store), __ATTR(wf_s7, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_wf_s7_show, qpnp_hap_wf_s7_store), __ATTR(wf_update, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_wf_update_show, qpnp_hap_wf_update_store), __ATTR(wf_rep, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_wf_rep_show, qpnp_hap_wf_rep_store), __ATTR(wf_s_rep, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_wf_s_rep_show, qpnp_hap_wf_s_rep_store), __ATTR(play_mode, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_play_mode_show, qpnp_hap_play_mode_store), __ATTR(dump_regs, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_dump_regs_show, NULL), __ATTR(ramp_test, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_ramp_test_data_show, qpnp_hap_ramp_test_data_store), __ATTR(min_max_test, (S_IRUGO | S_IWUSR | S_IWGRP), qpnp_hap_min_max_test_data_show, qpnp_hap_min_max_test_data_store), }; static void calculate_lra_code(struct qpnp_hap *hap) { u8 play_rate_code_lo, play_rate_code_hi; int play_rate_code, neg_idx = 0, pos_idx = LRA_POS_FREQ_COUNT-1; int lra_init_freq, freq_variation, start_variation = AUTO_RES_ERR_MAX; qpnp_hap_read_reg(hap, &play_rate_code_lo, QPNP_HAP_RATE_CFG1_REG(hap->base)); qpnp_hap_read_reg(hap, &play_rate_code_hi, QPNP_HAP_RATE_CFG2_REG(hap->base)); play_rate_code = (play_rate_code_hi << 8) | (play_rate_code_lo & 0xff); lra_init_freq = 200000 / play_rate_code; while (start_variation >= AUTO_RES_ERR_CAPTURE_RES) { freq_variation = (lra_init_freq * start_variation) / 100; lra_play_rate_code[neg_idx++] = 200000 / (lra_init_freq - freq_variation); lra_play_rate_code[pos_idx--] = 200000 / (lra_init_freq + freq_variation); start_variation -= AUTO_RES_ERR_CAPTURE_RES; } } static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) { int rc = 0; u8 val; rc = qpnp_hap_read_reg(hap, &val, QPNP_HAP_TEST2_REG(hap->base)); if (rc < 0) return rc; val &= QPNP_HAP_TEST2_AUTO_RES_MASK; if (enable) val |= AUTO_RES_ENABLE; else val |= AUTO_RES_DISABLE; /* TEST2 is a secure access register */ rc = qpnp_hap_sec_access(hap); if (rc) return rc; rc = qpnp_hap_write_reg(hap, &val, QPNP_HAP_TEST2_REG(hap->base)); if (rc) return rc; return 0; } static void update_lra_frequency(struct qpnp_hap *hap) { u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0; qpnp_hap_read_reg(hap, &lra_auto_res_lo, QPNP_HAP_LRA_AUTO_RES_LO(hap->base)); qpnp_hap_read_reg(hap, &lra_auto_res_hi, QPNP_HAP_LRA_AUTO_RES_HI(hap->base)); if (lra_auto_res_lo && lra_auto_res_hi) { qpnp_hap_write_reg(hap, &lra_auto_res_lo, QPNP_HAP_RATE_CFG1_REG(hap->base)); lra_auto_res_hi = lra_auto_res_hi >> 4; qpnp_hap_write_reg(hap, &lra_auto_res_hi, QPNP_HAP_RATE_CFG2_REG(hap->base)); } } static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) { struct qpnp_hap *hap = container_of(timer, struct qpnp_hap, auto_res_err_poll_timer); u8 val; ktime_t currtime; qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); if (val & AUTO_RES_ERR_BIT) { schedule_work(&hap->auto_res_err_work); return HRTIMER_NORESTART; } else { update_lra_frequency(hap); currtime = ktime_get(); hrtimer_forward(&hap->auto_res_err_poll_timer, currtime, ktime_set(0, POLL_TIME_AUTO_RES_ERR_NS)); return HRTIMER_RESTART; } } static void correct_auto_res_error(struct work_struct *auto_res_err_work) { struct qpnp_hap *hap = container_of(auto_res_err_work, struct qpnp_hap, auto_res_err_work); u8 lra_code_lo, lra_code_hi, disable_hap = 0x00; static int lra_freq_index; ktime_t currtime, remaining_time; int temp, rem = 0, index = lra_freq_index % LRA_POS_FREQ_COUNT; if (hrtimer_active(&hap->hap_timer)) { remaining_time = hrtimer_get_remaining(&hap->hap_timer); rem = (int)ktime_to_us(remaining_time); } qpnp_hap_play(hap, 0); qpnp_hap_write_reg(hap, &disable_hap, QPNP_HAP_EN_CTL_REG(hap->base)); lra_code_lo = lra_play_rate_code[index] & QPNP_HAP_RATE_CFG1_MASK; qpnp_hap_write_reg(hap, &lra_code_lo, QPNP_HAP_RATE_CFG1_REG(hap->base)); qpnp_hap_read_reg(hap, &lra_code_hi, QPNP_HAP_RATE_CFG2_REG(hap->base)); lra_code_hi &= QPNP_HAP_RATE_CFG2_MASK; temp = lra_play_rate_code[index] >> QPNP_HAP_RATE_CFG2_SHFT; lra_code_hi |= temp; qpnp_hap_write_reg(hap, &lra_code_hi, QPNP_HAP_RATE_CFG2_REG(hap->base)); lra_freq_index++; if (rem > 0) { currtime = ktime_get(); hap->state = 1; hrtimer_forward(&hap->hap_timer, currtime, remaining_time); schedule_work(&hap->work); } } /* set api for haptics */ static int qpnp_hap_set(struct qpnp_hap *hap, int on) { int rc = 0; u8 val = 0; unsigned long timeout_ns = POLL_TIME_AUTO_RES_ERR_NS; if (hap->play_mode == QPNP_HAP_PWM) { if (on) rc = pwm_enable(hap->pwm_info.pwm_dev); else pwm_disable(hap->pwm_info.pwm_dev); } else if (hap->play_mode == QPNP_HAP_BUFFER || hap->play_mode == QPNP_HAP_DIRECT) { if (on) { if (hap->correct_lra_drive_freq || hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) qpnp_hap_auto_res_enable(hap, 0); rc = qpnp_hap_mod_enable(hap, on); if (rc < 0) return rc; rc = qpnp_hap_play(hap, on); if ((hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) || hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) { usleep_range(AUTO_RES_ENABLE_TIMEOUT, (AUTO_RES_ENABLE_TIMEOUT + 1)); rc = qpnp_hap_auto_res_enable(hap, 1); if (rc < 0) return rc; } if (hap->correct_lra_drive_freq) { /* * Start timer to poll Auto Resonance error bit */ mutex_lock(&hap->lock); hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_start(&hap->auto_res_err_poll_timer, ktime_set(0, timeout_ns), HRTIMER_MODE_REL); mutex_unlock(&hap->lock); } } else { rc = qpnp_hap_play(hap, on); if (rc < 0) return rc; if (hap->correct_lra_drive_freq) { rc = qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); if (!(val & AUTO_RES_ERR_BIT)) update_lra_frequency(hap); } rc = qpnp_hap_mod_enable(hap, on); if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) { hrtimer_cancel(&hap->auto_res_err_poll_timer); calculate_lra_code(hap); } } } return rc; } /* enable interface from timed output class */ static void qpnp_hap_td_enable(struct timed_output_dev *dev, int value) { struct qpnp_hap *hap = container_of(dev, struct qpnp_hap, timed_dev); mutex_lock(&hap->lock); if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); if (value == 0) { if (hap->state == 0) { mutex_unlock(&hap->lock); return; } hap->state = 0; } else { value = (value > hap->timeout_ms ? hap->timeout_ms : value); hap->state = 1; hrtimer_start(&hap->hap_timer, ktime_set(value / 1000, (value % 1000) * 1000000), HRTIMER_MODE_REL); } mutex_unlock(&hap->lock); schedule_work(&hap->work); } /* play pwm bytes */ int qpnp_hap_play_byte(u8 data, bool on) { struct qpnp_hap *hap = ghap; int duty_ns, period_ns, duty_percent, rc; if (!hap) { pr_err("Haptics is not initialized\n"); return -EINVAL; } if (hap->play_mode != QPNP_HAP_PWM) { dev_err(&hap->spmi->dev, "only PWM mode is supported\n"); return -EINVAL; } rc = qpnp_hap_set(hap, false); if (rc) return rc; if (!on) { /* set the pwm back to original duty for normal operations */ /* this is not required if standard interface is not used */ rc = pwm_config(hap->pwm_info.pwm_dev, hap->pwm_info.duty_us * NSEC_PER_USEC, hap->pwm_info.period_us * NSEC_PER_USEC); return rc; } /* pwm values range from 0x00 to 0xff. The range from 0x00 to 0x7f provides a postive amplitude in the sin wave form for 0 to 100%. The range from 0x80 to 0xff provides a negative amplitude in the sin wave form for 0 to 100%. Here the duty percentage is calculated based on the incoming data to accommodate this. */ if (data <= QPNP_HAP_EXT_PWM_PEAK_DATA) duty_percent = QPNP_HAP_EXT_PWM_HALF_DUTY + ((data * QPNP_HAP_EXT_PWM_DATA_FACTOR) / 100); else duty_percent = QPNP_HAP_EXT_PWM_FULL_DUTY - ((data * QPNP_HAP_EXT_PWM_DATA_FACTOR) / 100); period_ns = hap->pwm_info.period_us * NSEC_PER_USEC; duty_ns = (period_ns * duty_percent) / 100; rc = pwm_config(hap->pwm_info.pwm_dev, duty_ns, hap->pwm_info.period_us * NSEC_PER_USEC); if (rc) return rc; dev_dbg(&hap->spmi->dev, "data=0x%x duty_per=%d\n", data, duty_percent); rc = qpnp_hap_set(hap, true); return rc; } EXPORT_SYMBOL(qpnp_hap_play_byte); /* worker to opeate haptics */ static void qpnp_hap_worker(struct work_struct *work) { struct qpnp_hap *hap = container_of(work, struct qpnp_hap, work); u8 val = 0x00; int rc, reg_en; if (hap->vcc_pon) { reg_en = regulator_enable(hap->vcc_pon); if (reg_en) pr_err("%s: could not enable vcc_pon regulator\n", __func__); } /* Disable haptics module if the duration of short circuit * exceeds the maximum limit (5 secs). */ if (hap->sc_duration == SC_MAX_DURATION) { rc = qpnp_hap_write_reg(hap, &val, QPNP_HAP_EN_CTL_REG(hap->base)); } else { if (hap->play_mode == QPNP_HAP_PWM) qpnp_hap_mod_enable(hap, hap->state); qpnp_hap_set(hap, hap->state); } if (hap->vcc_pon && !reg_en) { rc = regulator_disable(hap->vcc_pon); if (rc) pr_err("%s: could not disable vcc_pon regulator\n", __func__); } } /* get time api to know the remaining time */ static int qpnp_hap_get_time(struct timed_output_dev *dev) { struct qpnp_hap *hap = container_of(dev, struct qpnp_hap, timed_dev); if (hrtimer_active(&hap->hap_timer)) { ktime_t r = hrtimer_get_remaining(&hap->hap_timer); return (int)ktime_to_us(r); } else { return 0; } } /* hrtimer function handler */ static enum hrtimer_restart qpnp_hap_timer(struct hrtimer *timer) { struct qpnp_hap *hap = container_of(timer, struct qpnp_hap, hap_timer); hap->state = 0; schedule_work(&hap->work); return HRTIMER_NORESTART; } /* hrtimer function handler */ static enum hrtimer_restart qpnp_hap_test_timer(struct hrtimer *timer) { struct qpnp_hap *hap = container_of(timer, struct qpnp_hap, hap_test_timer); complete(&hap->completion); return HRTIMER_NORESTART; } /* suspend routines to turn off haptics */ #ifdef CONFIG_PM static int qpnp_haptic_suspend(struct device *dev) { struct qpnp_hap *hap = dev_get_drvdata(dev); hrtimer_cancel(&hap->hap_timer); cancel_work_sync(&hap->work); /* turn-off haptic */ qpnp_hap_set(hap, 0); return 0; } #endif static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL); /* Configuration api for haptics registers */ static int qpnp_hap_config(struct qpnp_hap *hap) { u8 reg = 0, error_code = 0, unlock_val, error_value; int rc, i, temp; /* Configure the ACTUATOR TYPE register */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_ACT_TYPE_MASK; reg |= hap->act_type; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base)); if (rc) return rc; /* Configure auto resonance parameters */ if (hap->act_type == QPNP_HAP_LRA) { if (hap->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN) hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN; else if (hap->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX) hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_AUTO_RES_MODE_MASK; reg |= (hap->auto_res_mode << QPNP_HAP_AUTO_RES_MODE_SHIFT); reg &= QPNP_HAP_LRA_HIGH_Z_MASK; reg |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT); reg &= QPNP_HAP_LRA_RES_CAL_PER_MASK; temp = fls(hap->lra_res_cal_period) - 1; reg |= (temp - 2); rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); if (rc) return rc; } else { /* disable auto resonance for ERM */ reg = 0x00; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); if (rc) return rc; } /* Configure the PLAY MODE register */ rc = qpnp_hap_play_mode_config(hap); if (rc) return rc; /* Configure the VMAX register */ rc = qpnp_hap_vmax_config(hap); if (rc) return rc; /* Configure the ILIM register */ if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA) hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA; else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA) hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ILIM_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_ILIM_MASK; temp = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) >> 1; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_ILIM_REG(hap->base)); if (rc) return rc; /* Configure the short circuit debounce register */ rc = qpnp_hap_sc_deb_config(hap); if (rc) return rc; /* Configure the INTERNAL_PWM register */ if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) { hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ; temp = 0; } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) { hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; temp = 1; } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) { hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ; temp = 2; } else { hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ; temp = 3; } rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_INT_PWM_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_INT_PWM_MASK; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_INT_PWM_REG(hap->base)); if (rc) return rc; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PWM_CAP_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_INT_PWM_MASK; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_PWM_CAP_REG(hap->base)); if (rc) return rc; /* Configure the WAVE SHAPE register */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_WAV_SHAPE_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_WAV_SHAPE_MASK; reg |= hap->wave_shape; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_WAV_SHAPE_REG(hap->base)); if (rc) return rc; /* Configure RATE_CFG1 and RATE_CFG2 registers */ /* Note: For ERM these registers act as play rate and for LRA these represent resonance period */ if (hap->wave_play_rate_us < QPNP_HAP_WAV_PLAY_RATE_US_MIN) hap->wave_play_rate_us = QPNP_HAP_WAV_PLAY_RATE_US_MIN; else if (hap->wave_play_rate_us > QPNP_HAP_WAV_PLAY_RATE_US_MAX) hap->wave_play_rate_us = QPNP_HAP_WAV_PLAY_RATE_US_MAX; temp = hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US; /* * The frequency of 19.2Mzhz RC clock is subject to variation. * In PMI8950, TRIM_ERROR_RC19P2_CLK register in MISC module * holds the frequency error in 19.2Mhz RC clock */ if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq && hap->misc_trim_error_rc19p2_clk_reg_present) { unlock_val = MISC_SEC_UNLOCK; rc = spmi_ext_register_writel(hap->spmi->ctrl, PMI8950_MISC_SID, MISC_SEC_ACCESS, &unlock_val, 1); if (rc) dev_err(&hap->spmi->dev, "Unable to do SEC_ACCESS rc:%d\n", rc); spmi_ext_register_readl(hap->spmi->ctrl, PMI8950_MISC_SID, MISC_TRIM_ERROR_RC19P2_CLK, &error_code, 1); error_value = (error_code & 0x0F) * 7; if (error_code & 0x80) temp = (temp * (1000 - error_value)) / 1000; else temp = (temp * (1000 + error_value)) / 1000; } reg = temp & QPNP_HAP_RATE_CFG1_MASK; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_RATE_CFG1_REG(hap->base)); if (rc) return rc; rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_RATE_CFG2_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_RATE_CFG2_MASK; temp = temp >> QPNP_HAP_RATE_CFG2_SHFT; reg |= temp; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_RATE_CFG2_REG(hap->base)); if (rc) return rc; if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq) calculate_lra_code(hap); /* Configure BRAKE register */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); if (rc < 0) return rc; reg &= QPNP_HAP_BRAKE_MASK; reg |= hap->en_brake; rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); if (rc) return rc; if (hap->en_brake && hap->sup_brake_pat) { for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, reg = 0; i >= 0; i--) { hap->brake_pat[i] &= QPNP_HAP_BRAKE_PAT_MASK; temp = i << 1; reg |= hap->brake_pat[i] << temp; } rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_BRAKE_REG(hap->base)); if (rc) return rc; } /* Cache enable control register */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL_REG(hap->base)); if (rc < 0) return rc; hap->reg_en_ctl = reg; /* Cache play register */ rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PLAY_REG(hap->base)); if (rc < 0) return rc; hap->reg_play = reg; if (hap->play_mode == QPNP_HAP_BUFFER) rc = qpnp_hap_buffer_config(hap); else if (hap->play_mode == QPNP_HAP_PWM) rc = qpnp_hap_pwm_config(hap); else if (hap->play_mode == QPNP_HAP_AUDIO) rc = qpnp_hap_mod_enable(hap, true); if (rc) return rc; /* setup short circuit irq */ if (hap->use_sc_irq) { rc = devm_request_threaded_irq(&hap->spmi->dev, hap->sc_irq, NULL, qpnp_hap_sc_irq, QPNP_IRQ_FLAGS, "qpnp_sc_irq", hap); if (rc < 0) { dev_err(&hap->spmi->dev, "Unable to request sc(%d) IRQ(err:%d)\n", hap->sc_irq, rc); return rc; } } hap->sc_duration = 0; return rc; } /* DT parsing for haptics parameters */ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) { struct spmi_device *spmi = hap->spmi; struct property *prop; const char *temp_str; u32 temp; int rc; hap->timeout_ms = QPNP_HAP_TIMEOUT_MS_MAX; rc = of_property_read_u32(spmi->dev.of_node, "qcom,timeout-ms", &temp); if (!rc) { hap->timeout_ms = temp; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read timeout\n"); return rc; } hap->act_type = QPNP_HAP_LRA; rc = of_property_read_string(spmi->dev.of_node, "qcom,actuator-type", &temp_str); if (!rc) { if (strcmp(temp_str, "erm") == 0) hap->act_type = QPNP_HAP_ERM; else if (strcmp(temp_str, "lra") == 0) hap->act_type = QPNP_HAP_LRA; else { dev_err(&spmi->dev, "Invalid actuator type\n"); return -EINVAL; } } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read actuator type\n"); return rc; } if (hap->act_type == QPNP_HAP_LRA) { hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; rc = of_property_read_string(spmi->dev.of_node, "qcom,lra-auto-res-mode", &temp_str); if (!rc) { if (strcmp(temp_str, "none") == 0) hap->auto_res_mode = QPNP_HAP_AUTO_RES_NONE; else if (strcmp(temp_str, "zxd") == 0) hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD; else if (strcmp(temp_str, "qwd") == 0) hap->auto_res_mode = QPNP_HAP_AUTO_RES_QWD; else if (strcmp(temp_str, "max-qwd") == 0) hap->auto_res_mode = QPNP_HAP_AUTO_RES_MAX_QWD; else hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read auto res mode\n"); return rc; } hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3; rc = of_property_read_string(spmi->dev.of_node, "qcom,lra-high-z", &temp_str); if (!rc) { if (strcmp(temp_str, "none") == 0) hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_NONE; else if (strcmp(temp_str, "opt1") == 0) hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1; else if (strcmp(temp_str, "opt2") == 0) hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT2; else hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read LRA high-z\n"); return rc; } hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; rc = of_property_read_u32(spmi->dev.of_node, "qcom,lra-res-cal-period", &temp); if (!rc) { hap->lra_res_cal_period = temp; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read cal period\n"); return rc; } hap->correct_lra_drive_freq = of_property_read_bool(spmi->dev.of_node, "qcom,correct-lra-drive-freq"); hap->misc_trim_error_rc19p2_clk_reg_present = of_property_read_bool(spmi->dev.of_node, "qcom,misc-trim-error-rc19p2-clk-reg-present"); } rc = of_property_read_string(spmi->dev.of_node, "qcom,play-mode", &temp_str); if (!rc) { if (strcmp(temp_str, "direct") == 0) hap->play_mode = QPNP_HAP_DIRECT; else if (strcmp(temp_str, "buffer") == 0) hap->play_mode = QPNP_HAP_BUFFER; else if (strcmp(temp_str, "pwm") == 0) hap->play_mode = QPNP_HAP_PWM; else if (strcmp(temp_str, "audio") == 0) hap->play_mode = QPNP_HAP_AUDIO; else { dev_err(&spmi->dev, "Invalid play mode\n"); return -EINVAL; } } else { dev_err(&spmi->dev, "Unable to read play mode\n"); return rc; } hap->vmax_mv = QPNP_HAP_VMAX_MAX_MV; rc = of_property_read_u32(spmi->dev.of_node, "qcom,vmax-mv", &temp); if (!rc) { hap->vmax_mv = temp; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read vmax\n"); return rc; } hap->ilim_ma = QPNP_HAP_ILIM_MIN_MV; rc = of_property_read_u32(spmi->dev.of_node, "qcom,ilim-ma", &temp); if (!rc) { hap->ilim_ma = temp; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read ILim\n"); return rc; } hap->sc_deb_cycles = QPNP_HAP_DEF_SC_DEB_CYCLES; rc = of_property_read_u32(spmi->dev.of_node, "qcom,sc-deb-cycles", &temp); if (!rc) { hap->sc_deb_cycles = temp; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read sc debounce\n"); return rc; } hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; rc = of_property_read_u32(spmi->dev.of_node, "qcom,int-pwm-freq-khz", &temp); if (!rc) { hap->int_pwm_freq_khz = temp; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read int pwm freq\n"); return rc; } hap->wave_shape = QPNP_HAP_WAV_SQUARE; rc = of_property_read_string(spmi->dev.of_node, "qcom,wave-shape", &temp_str); if (!rc) { if (strcmp(temp_str, "sine") == 0) hap->wave_shape = QPNP_HAP_WAV_SINE; else if (strcmp(temp_str, "square") == 0) hap->wave_shape = QPNP_HAP_WAV_SQUARE; else { dev_err(&spmi->dev, "Unsupported wav shape\n"); return -EINVAL; } } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read wav shape\n"); return rc; } hap->wave_play_rate_us = QPNP_HAP_DEF_WAVE_PLAY_RATE_US; rc = of_property_read_u32(spmi->dev.of_node, "qcom,wave-play-rate-us", &temp); if (!rc) { hap->wave_play_rate_us = temp; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read play rate\n"); return rc; } if (hap->play_mode == QPNP_HAP_BUFFER) rc = qpnp_hap_parse_buffer_dt(hap); else if (hap->play_mode == QPNP_HAP_PWM) rc = qpnp_hap_parse_pwm_dt(hap); if (rc < 0) return rc; hap->en_brake = of_property_read_bool(spmi->dev.of_node, "qcom,en-brake"); if (hap->en_brake) { prop = of_find_property(spmi->dev.of_node, "qcom,brake-pattern", &temp); if (!prop) { dev_info(&spmi->dev, "brake pattern not found"); } else if (temp != QPNP_HAP_BRAKE_PAT_LEN) { dev_err(&spmi->dev, "Invalid len of brake pattern\n"); return -EINVAL; } else { hap->sup_brake_pat = true; memcpy(hap->brake_pat, prop->value, QPNP_HAP_BRAKE_PAT_LEN); } } hap->use_sc_irq = of_property_read_bool(spmi->dev.of_node, "qcom,use-sc-irq"); if (hap->use_sc_irq) { hap->sc_irq = spmi_get_irq_byname(hap->spmi, NULL, "sc-irq"); if (hap->sc_irq < 0) { dev_err(&spmi->dev, "Unable to get sc irq\n"); return hap->sc_irq; } } if (of_find_property(spmi->dev.of_node, "vcc_pon-supply", NULL)) hap->manage_pon_supply = true; return 0; } static int qpnp_haptic_probe(struct spmi_device *spmi) { struct qpnp_hap *hap; struct resource *hap_resource; struct regulator *vcc_pon; int rc, i; hap = devm_kzalloc(&spmi->dev, sizeof(*hap), GFP_KERNEL); if (!hap) return -ENOMEM; hap->spmi = spmi; hap_resource = spmi_get_resource(spmi, 0, IORESOURCE_MEM, 0); if (!hap_resource) { dev_err(&spmi->dev, "Unable to get haptic base address\n"); return -EINVAL; } hap->base = hap_resource->start; dev_set_drvdata(&spmi->dev, hap); rc = qpnp_hap_parse_dt(hap); if (rc) { dev_err(&spmi->dev, "DT parsing failed\n"); return rc; } rc = qpnp_hap_config(hap); if (rc) { dev_err(&spmi->dev, "hap config failed\n"); return rc; } mutex_init(&hap->lock); mutex_init(&hap->wf_lock); INIT_WORK(&hap->work, qpnp_hap_worker); INIT_DELAYED_WORK(&hap->sc_work, qpnp_handle_sc_irq); init_completion(&hap->completion); hrtimer_init(&hap->hap_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hap->hap_timer.function = qpnp_hap_timer; hrtimer_init(&hap->hap_test_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hap->hap_test_timer.function = qpnp_hap_test_timer; hap->timed_dev.name = "vibrator"; hap->timed_dev.get_time = qpnp_hap_get_time; hap->timed_dev.enable = qpnp_hap_td_enable; if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) { INIT_WORK(&hap->auto_res_err_work, correct_auto_res_error); hrtimer_init(&hap->auto_res_err_poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hap->auto_res_err_poll_timer.function = detect_auto_res_error; } rc = timed_output_dev_register(&hap->timed_dev); if (rc < 0) { dev_err(&spmi->dev, "timed_output registration failed\n"); goto timed_output_fail; } for (i = 0; i < ARRAY_SIZE(qpnp_hap_attrs); i++) { rc = sysfs_create_file(&hap->timed_dev.dev->kobj, &qpnp_hap_attrs[i].attr); if (rc < 0) { dev_err(&spmi->dev, "sysfs creation failed\n"); goto sysfs_fail; } } if (hap->manage_pon_supply) { vcc_pon = regulator_get(&spmi->dev, "vcc_pon"); if (IS_ERR(vcc_pon)) { rc = PTR_ERR(vcc_pon); dev_err(&spmi->dev, "regulator get failed vcc_pon rc=%d\n", rc); goto sysfs_fail; } hap->vcc_pon = vcc_pon; } ghap = hap; return 0; sysfs_fail: for (i--; i >= 0; i--) sysfs_remove_file(&hap->timed_dev.dev->kobj, &qpnp_hap_attrs[i].attr); timed_output_dev_unregister(&hap->timed_dev); timed_output_fail: cancel_work_sync(&hap->work); if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); mutex_destroy(&hap->lock); mutex_destroy(&hap->wf_lock); return rc; } static int qpnp_haptic_remove(struct spmi_device *spmi) { struct qpnp_hap *hap = dev_get_drvdata(&spmi->dev); int i; for (i = 0; i < ARRAY_SIZE(qpnp_hap_attrs); i++) sysfs_remove_file(&hap->timed_dev.dev->kobj, &qpnp_hap_attrs[i].attr); cancel_work_sync(&hap->work); if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); timed_output_dev_unregister(&hap->timed_dev); mutex_destroy(&hap->lock); mutex_destroy(&hap->wf_lock); if (hap->vcc_pon) regulator_put(hap->vcc_pon); return 0; } static struct of_device_id spmi_match_table[] = { { .compatible = "qcom,qpnp-haptic", }, { }, }; static struct spmi_driver qpnp_haptic_driver = { .driver = { .name = "qcom,qpnp-haptic", .of_match_table = spmi_match_table, .pm = &qpnp_haptic_pm_ops, }, .probe = qpnp_haptic_probe, .remove = qpnp_haptic_remove, }; static int __init qpnp_haptic_init(void) { return spmi_driver_register(&qpnp_haptic_driver); } module_init(qpnp_haptic_init); static void __exit qpnp_haptic_exit(void) { return spmi_driver_unregister(&qpnp_haptic_driver); } module_exit(qpnp_haptic_exit); MODULE_DESCRIPTION("qpnp haptic driver"); MODULE_LICENSE("GPL v2");