OSDN Git Service

soc: qcom: qpnp-haptic: add support for auto mode
authorSubbaraman Narayanamurthy <subbaram@codeaurora.org>
Tue, 7 Mar 2017 00:23:23 +0000 (16:23 -0800)
committerSubbaraman Narayanamurthy <subbaram@codeaurora.org>
Tue, 9 May 2017 18:00:36 +0000 (11:00 -0700)
Based on the duration of the pattern, haptics mode and other
parameters needs to be configured dynamically for a better
performance and ease of use. Add support for this through device
tree property "qcom,lra-auto-mode".

Add support to configure some parameters like auto resonance
mode, LRA high-Z period, calibration period, vmax along with an
option to override the settings done by auto_mode configuration.

Change-Id: I4706fed4e3ca97d190f907769fd4a9899479b330
Signed-off-by: Subbaraman Narayanamurthy <subbaram@codeaurora.org>
Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt
drivers/soc/qcom/qpnp-haptic.c

index 3376498..aa3363b 100644 (file)
@@ -76,6 +76,10 @@ Optional properties when qcom,actuator-type is "lra"
                        at End of Pattern
  - qcom,lra-res-cal-period : Auto resonance calibration period. The values range from
                        4 to 32(default)
+- qcom,lra-auto-mode : If this property is specified, haptics mode for LRA
+                      actuators will be automatically configured along with
+                      other required settings runtime based on the duration
+                      of the pattern.
  - qcom,perform-lra-auto-resonance-search : boolean, define this property if:
                a) the underlying PMI chip does not have a register in the MISC block to
                read the error percentage in RC clock
index 70cf113..d86f867 100644 (file)
@@ -85,6 +85,7 @@
 #define QPNP_HAP_PM660_RES_CAL_PERIOD_MAX      256
 #define QPNP_HAP_WF_SOURCE_MASK                GENMASK(5, 4)
 #define QPNP_HAP_WF_SOURCE_SHIFT       4
+#define QPNP_HAP_VMAX_OVD_BIT          BIT(6)
 #define QPNP_HAP_VMAX_MASK             GENMASK(5, 1)
 #define QPNP_HAP_VMAX_SHIFT            1
 #define QPNP_HAP_VMAX_MIN_MV           116
 #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_WF_AMP_MASK           GENMASK(5, 1)
+#define QPNP_HAP_WF_OVD_BIT            BIT(6)
 #define QPNP_HAP_BRAKE_PAT_MASK                0x3
 #define QPNP_HAP_ILIM_MIN_MA           400
 #define QPNP_HAP_ILIM_MAX_MA           800
 #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_WAV_SAMP_MAX          0x3E
 #define QPNP_HAP_BRAKE_PAT_LEN         4
-#define QPNP_HAP_PLAY_EN               0x80
+#define QPNP_HAP_PLAY_EN_BIT           BIT(7)
 #define QPNP_HAP_EN_BIT                        BIT(7)
 #define QPNP_HAP_BRAKE_MASK            BIT(0)
 #define QPNP_HAP_AUTO_RES_MASK         BIT(7)
 #define AUTO_RES_ENABLE                        BIT(7)
 #define AUTO_RES_ERR_BIT               0x10
 #define SC_FOUND_BIT                   0x08
-#define SC_MAX_DURATION                        5
+#define SC_MAX_COUNT                   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 QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN     20000
@@ -262,6 +264,22 @@ struct qpnp_pwm_info {
 };
 
 /*
+ *  qpnp_hap_lra_ares_cfg - Haptic auto_resonance configuration
+ *  @ lra_qwd_drive_duration - LRA QWD drive duration
+ *  @ calibrate_at_eop - Calibrate at EOP
+ *  @ lra_res_cal_period - LRA resonance calibration period
+ *  @ auto_res_mode - auto resonace mode
+ *  @ lra_high_z - high z option line
+ */
+struct qpnp_hap_lra_ares_cfg {
+       int                             lra_qwd_drive_duration;
+       int                             calibrate_at_eop;
+       enum qpnp_hap_high_z            lra_high_z;
+       u16                             lra_res_cal_period;
+       u8                              auto_res_mode;
+};
+
+/*
  *  qpnp_hap - Haptic data structure
  *  @ spmi - spmi device
  *  @ hap_timer - hrtimer
@@ -270,6 +288,7 @@ struct qpnp_pwm_info {
  *  @ work - worker
  *  @ sc_work - worker to handle short circuit condition
  *  @ pwm_info - pwm info
+ *  @ ares_cfg - auto resonance configuration
  *  @ lock - mutex lock
  *  @ wf_lock - mutex lock for waveform
  *  @ init_drive_period_code - the initial lra drive period code
@@ -281,10 +300,7 @@ struct qpnp_pwm_info {
       percentage variation on the higher side.
  *  @ drive_period_code_min_limit - calculated drive period code with
       percentage variation on the lower side
- *  @ lra_res_cal_period - LRA resonance calibration period
  *  @ play_mode - play mode
- *  @ auto_res_mode - auto resonace mode
- *  @ lra_high_z - high z option line
  *  @ timeout_ms - max timeout in ms
  *  @ time_required_to_generate_back_emf_us - the time required for sufficient
       back-emf to be generated for auto resonance to be successful
@@ -293,6 +309,7 @@ struct qpnp_pwm_info {
  *  @ sc_deb_cycles - short circuit debounce cycles
  *  @ int_pwm_freq_khz - internal pwm frequency in khz
  *  @ wave_play_rate_us - play rate for waveform
+ *  @ play_time_ms - play time set by the user
  *  @ ext_pwm_freq_khz - external pwm frequency in khz
  *  @ wave_rep_cnt - waveform repeat count
  *  @ wave_s_rep_cnt - waveform sample repeat count
@@ -305,14 +322,12 @@ struct qpnp_pwm_info {
  *  @ wave_samp - array of wave samples
  *  @ shadow_wave_samp - shadow array of wave samples
  *  @ brake_pat - pattern for active breaking
- *  @ reg_play - play register
- *  @ lra_res_cal_period - period for resonance calibration
- *  @ sc_duration - counter to determine the duration of short circuit condition
+ *  @ sc_count - counter to determine the duration of short circuit condition
  *  @ lra_hw_auto_resonance - enable hardware auto resonance
  *  @ state - current state of haptics
+ *  @ module_en - haptics module enable status
  *  @ 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
@@ -320,6 +335,9 @@ struct qpnp_pwm_info {
  *  @ clk_trim_error_code - MISC clock trim error code
  *  @ perform_lra_auto_resonance_search - whether lra auto resonance search
  *    algorithm should be performed or not.
+ *  @ auto_mode - Auto mode selection
+ *  @ override_auto_mode_config - Flag to override auto mode configuration with
+ *    user specified values through sysfs.
  */
 struct qpnp_hap {
        struct platform_device          *pdev;
@@ -333,14 +351,12 @@ struct qpnp_hap {
        struct hrtimer                  hap_test_timer;
        struct work_struct              test_work;
        struct qpnp_pwm_info            pwm_info;
+       struct qpnp_hap_lra_ares_cfg    ares_cfg;
        struct mutex                    lock;
        struct mutex                    wf_lock;
        spinlock_t                      bus_lock;
        struct completion               completion;
        enum qpnp_hap_mode              play_mode;
-       enum qpnp_hap_high_z            lra_high_z;
-       int                             lra_qwd_drive_duration;
-       int                             calibrate_at_eop;
        u32                             misc_clk_trim_error_reg;
        u32                             init_drive_period_code;
        u32                             timeout_ms;
@@ -350,6 +366,7 @@ struct qpnp_hap {
        u32                             sc_deb_cycles;
        u32                             int_pwm_freq_khz;
        u32                             wave_play_rate_us;
+       u32                             play_time_ms;
        u32                             ext_pwm_freq_khz;
        u32                             wave_rep_cnt;
        u32                             wave_s_rep_cnt;
@@ -360,7 +377,6 @@ struct qpnp_hap {
        u16                             last_rate_cfg;
        u16                             drive_period_code_max_limit;
        u16                             drive_period_code_min_limit;
-       u16                             lra_res_cal_period;
        u8                      drive_period_code_max_limit_percent_variation;
        u8                      drive_period_code_min_limit_percent_variation;
        u8                              act_type;
@@ -368,23 +384,24 @@ struct qpnp_hap {
        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_play;
-       u8                              sc_duration;
+       u8                              sc_count;
        u8                              ext_pwm_dtest_line;
        u8                              pmic_subtype;
-       u8                              auto_res_mode;
        u8                              clk_trim_error_code;
        bool                            lra_hw_auto_resonance;
        bool                            vcc_pon_enabled;
        bool                            state;
+       bool                            module_en;
        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                            perform_lra_auto_resonance_search;
+       bool                            auto_mode;
+       bool                            override_auto_mode_config;
+       bool                            play_irq_en;
 };
 
 static struct qpnp_hap *ghap;
@@ -502,36 +519,50 @@ static void qpnp_handle_sc_irq(struct work_struct *work)
 
        /* clear short circuit register */
        if (val & SC_FOUND_BIT) {
-               hap->sc_duration++;
+               hap->sc_count++;
                val = QPNP_HAP_SC_CLR;
                qpnp_hap_write_reg(hap, QPNP_HAP_SC_CLR_REG(hap->base), val);
        }
 }
 
+#define QPNP_HAP_CYCLES                4
 static int qpnp_hap_mod_enable(struct qpnp_hap *hap, bool on)
 {
+       unsigned long wait_time_us;
        u8 val;
        int rc, i;
 
+       if (hap->module_en == on)
+               return 0;
+
        if (!on) {
-               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;
+               /*
+                * Wait for 4 cycles of play rate for play time is > 20 ms and
+                * wait for play time when play time is < 20 ms. This way, there
+                * will be an improvement in waiting time polling BUSY status.
+                */
+               if (hap->play_time_ms <= 20)
+                       wait_time_us = hap->play_time_ms * 1000;
+               else
+                       wait_time_us = QPNP_HAP_CYCLES * hap->wave_play_rate_us;
 
+               for (i = 0; i < QPNP_HAP_MAX_RETRIES; i++) {
                        rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base),
                                        &val);
+                       if (rc < 0)
+                               return rc;
 
                        pr_debug("HAP_STATUS=0x%x\n", val);
 
-                       /* wait for QPNP_HAP_CYCLS cycles of play rate */
+                       /* wait for play_rate cycles */
                        if (val & QPNP_HAP_STATUS_BUSY) {
-                               usleep_range(sleep_time, sleep_time + 1);
+                               usleep_range(wait_time_us, wait_time_us + 1);
                                if (hap->play_mode == QPNP_HAP_DIRECT ||
                                        hap->play_mode == QPNP_HAP_PWM)
                                        break;
-                       } else
+                       } else {
                                break;
+                       }
                }
 
                if (i >= QPNP_HAP_MAX_RETRIES)
@@ -543,27 +574,18 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, bool on)
        if (rc < 0)
                return rc;
 
+       hap->module_en = on;
        return 0;
 }
 
-static int qpnp_hap_play(struct qpnp_hap *hap, int on)
+static int qpnp_hap_play(struct qpnp_hap *hap, bool on)
 {
        u8 val;
        int rc;
 
-       val = hap->reg_play;
-       if (on)
-               val |= QPNP_HAP_PLAY_EN;
-       else
-               val &= ~QPNP_HAP_PLAY_EN;
-
+       val = on ? QPNP_HAP_PLAY_EN_BIT : 0;
        rc = qpnp_hap_write_reg(hap, QPNP_HAP_PLAY_REG(hap->base), val);
-       if (rc < 0)
-               return rc;
-
-       hap->reg_play = val;
-
-       return 0;
+       return rc;
 }
 
 /* sysfs show debug registers */
@@ -624,13 +646,13 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap)
 
        pr_debug("Short circuit detected\n");
 
-       if (hap->sc_duration < SC_MAX_DURATION) {
+       if (hap->sc_count < SC_MAX_COUNT) {
                qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val);
                if (val & SC_FOUND_BIT)
                        schedule_delayed_work(&hap->sc_work,
                                        QPNP_HAP_SC_IRQ_STATUS_DELAY);
                else
-                       hap->sc_duration = 0;
+                       hap->sc_count = 0;
        } else {
                /* Disable haptics module if the duration of short circuit
                 * exceeds the maximum limit (5 secs).
@@ -645,9 +667,11 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap)
 }
 
 /* configuration api for buffer mode */
-static int qpnp_hap_buffer_config(struct qpnp_hap *hap)
+static int qpnp_hap_buffer_config(struct qpnp_hap *hap, u8 *wave_samp,
+                               bool overdrive)
 {
-       u8 val = 0;
+       u8 buf[QPNP_HAP_WAV_SAMP_LEN], val;
+       u8 *ptr;
        int rc, i;
 
        /* Configure the WAVE_REPEAT register */
@@ -668,16 +692,27 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap)
        if (rc)
                return rc;
 
+       /* Don't set override bit in waveform sample for PM660 */
+       if (hap->pmic_subtype == PM660_SUBTYPE)
+               overdrive = false;
+
+       if (wave_samp)
+               ptr = wave_samp;
+       else
+               ptr = hap->wave_samp;
+
        /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */
-       for (i = 0, val = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) {
-               val = hap->wave_samp[i];
-               rc = qpnp_hap_write_reg(hap,
-                       QPNP_HAP_WAV_S_REG_BASE(hap->base) + i, val);
-               if (rc)
-                       return rc;
+       for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) {
+               buf[i] = ptr[i] & QPNP_HAP_WF_AMP_MASK;
+               if (buf[i])
+                       buf[i] |= (overdrive ? QPNP_HAP_WF_OVD_BIT : 0);
        }
 
-       hap->buffer_cfg_state = true;
+       rc = qpnp_hap_write_mult_reg(hap, QPNP_HAP_WAV_S_REG_BASE(hap->base),
+                               buf, QPNP_HAP_WAV_SAMP_LEN);
+       if (rc)
+               return rc;
+
        return 0;
 }
 
@@ -736,10 +771,12 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap)
        return 0;
 }
 
-static int qpnp_hap_lra_auto_res_config(struct qpnp_hap *hap)
+static int qpnp_hap_lra_auto_res_config(struct qpnp_hap *hap,
+                                       struct qpnp_hap_lra_ares_cfg *tmp_cfg)
 {
+       struct qpnp_hap_lra_ares_cfg *ares_cfg;
        int rc;
-       u8 val, mask;
+       u8 val = 0, mask = 0;
 
        /* disable auto resonance for ERM */
        if (hap->act_type == QPNP_HAP_ERM) {
@@ -751,59 +788,73 @@ static int qpnp_hap_lra_auto_res_config(struct qpnp_hap *hap)
 
        if (hap->lra_hw_auto_resonance) {
                rc = qpnp_hap_masked_write_reg(hap,
-                       QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT,
                        QPNP_HAP_AUTO_RES_CTRL(hap->base),
+                       QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT,
                        QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT);
                if (rc)
                        return rc;
        }
 
-       if (hap->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN)
-               hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN;
+       if (tmp_cfg)
+               ares_cfg = tmp_cfg;
+       else
+               ares_cfg = &hap->ares_cfg;
+
+       if (ares_cfg->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN)
+               ares_cfg->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN;
 
        if (hap->pmic_subtype == PM660_SUBTYPE) {
-               if (hap->lra_res_cal_period >
+               if (ares_cfg->lra_res_cal_period >
                                QPNP_HAP_PM660_RES_CAL_PERIOD_MAX)
-                       hap->lra_res_cal_period =
+                       ares_cfg->lra_res_cal_period =
                                QPNP_HAP_PM660_RES_CAL_PERIOD_MAX;
 
-               if (hap->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD)
-                       hap->lra_res_cal_period = 0;
+               if (ares_cfg->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD)
+                       ares_cfg->lra_res_cal_period = 0;
+
+               if (ares_cfg->lra_res_cal_period)
+                       val = ilog2(ares_cfg->lra_res_cal_period /
+                                       QPNP_HAP_RES_CAL_PERIOD_MIN) + 1;
        } else {
-               if (hap->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX)
-                       hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX;
-       }
+               if (ares_cfg->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX)
+                       ares_cfg->lra_res_cal_period =
+                               QPNP_HAP_RES_CAL_PERIOD_MAX;
 
-       val = mask = 0;
-       if (hap->lra_res_cal_period)
-               val = ilog2(hap->lra_res_cal_period /
-                               QPNP_HAP_RES_CAL_PERIOD_MIN);
+               if (ares_cfg->lra_res_cal_period)
+                       val = ilog2(ares_cfg->lra_res_cal_period /
+                                       QPNP_HAP_RES_CAL_PERIOD_MIN);
+       }
 
        if (hap->pmic_subtype == PM660_SUBTYPE) {
-               val |= hap->auto_res_mode <<
+               val |= ares_cfg->auto_res_mode <<
                        QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT;
                mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT;
-               val |= hap->lra_high_z <<
+               val |= ares_cfg->lra_high_z <<
                                QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT;
                mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK;
-               if (hap->lra_qwd_drive_duration != -EINVAL) {
-                       val |= hap->lra_qwd_drive_duration <<
+               if (ares_cfg->lra_qwd_drive_duration != -EINVAL) {
+                       val |= ares_cfg->lra_qwd_drive_duration <<
                                QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT;
                        mask |= QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT;
                }
-               if (hap->calibrate_at_eop != -EINVAL) {
-                       val |= hap->calibrate_at_eop <<
+               if (ares_cfg->calibrate_at_eop != -EINVAL) {
+                       val |= ares_cfg->calibrate_at_eop <<
                                QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT;
                        mask |= QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT;
                }
                mask |= QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK;
        } else {
-               val |= (hap->auto_res_mode << QPNP_HAP_AUTO_RES_MODE_SHIFT);
-               val |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT);
+               val |= (ares_cfg->auto_res_mode <<
+                               QPNP_HAP_AUTO_RES_MODE_SHIFT);
+               val |= (ares_cfg->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT);
                mask = QPNP_HAP_AUTO_RES_MODE_MASK | QPNP_HAP_LRA_HIGH_Z_MASK |
                        QPNP_HAP_LRA_RES_CAL_PER_MASK;
        }
 
+       pr_debug("mode: %d hi_z period: %d cal_period: %d\n",
+               ares_cfg->auto_res_mode, ares_cfg->lra_high_z,
+               ares_cfg->lra_res_cal_period);
+
        rc = qpnp_hap_masked_write_reg(hap,
                        QPNP_HAP_LRA_AUTO_RES_REG(hap->base), mask, val);
        return rc;
@@ -822,19 +873,29 @@ static int qpnp_hap_play_mode_config(struct qpnp_hap *hap)
 }
 
 /* configuration api for max voltage */
-static int qpnp_hap_vmax_config(struct qpnp_hap *hap)
+static int qpnp_hap_vmax_config(struct qpnp_hap *hap, int vmax_mv,
+                               bool overdrive)
 {
        u8 val = 0;
        int rc;
 
-       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;
+       if (vmax_mv < 0)
+               return -EINVAL;
 
-       val = (hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV) << QPNP_HAP_VMAX_SHIFT;
+       /* Allow setting override bit in VMAX_CFG only for PM660 */
+       if (hap->pmic_subtype != PM660_SUBTYPE)
+               overdrive = false;
+
+       if (vmax_mv < QPNP_HAP_VMAX_MIN_MV)
+               vmax_mv = QPNP_HAP_VMAX_MIN_MV;
+       else if (vmax_mv > QPNP_HAP_VMAX_MAX_MV)
+               vmax_mv = QPNP_HAP_VMAX_MAX_MV;
+
+       val = (vmax_mv / QPNP_HAP_VMAX_MIN_MV) << QPNP_HAP_VMAX_SHIFT;
+       if (overdrive)
+               val |= QPNP_HAP_VMAX_OVD_BIT;
        rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_VMAX_REG(hap->base),
-                       QPNP_HAP_VMAX_MASK, val);
+                       QPNP_HAP_VMAX_MASK | QPNP_HAP_VMAX_OVD_BIT, val);
        return rc;
 }
 
@@ -912,6 +973,41 @@ static int qpnp_hap_int_pwm_config(struct qpnp_hap *hap)
        return rc;
 }
 
+static int qpnp_hap_brake_config(struct qpnp_hap *hap, u8 *brake_pat)
+{
+       int rc, i;
+       u32 temp;
+       u8 *pat_ptr, val;
+
+       if (!hap->en_brake)
+               return 0;
+
+       /* Configure BRAKE register */
+       rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EN_CTL2_REG(hap->base),
+                       QPNP_HAP_BRAKE_MASK, (u8)hap->en_brake);
+       if (rc)
+               return rc;
+
+       if (!brake_pat)
+               pat_ptr = hap->brake_pat;
+       else
+               pat_ptr = brake_pat;
+
+       if (hap->sup_brake_pat) {
+               for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, val = 0; i >= 0; i--) {
+                       pat_ptr[i] &= QPNP_HAP_BRAKE_PAT_MASK;
+                       temp = i << 1;
+                       val |= pat_ptr[i] << temp;
+               }
+               rc = qpnp_hap_write_reg(hap, QPNP_HAP_BRAKE_REG(hap->base),
+                               val);
+               if (rc)
+                       return rc;
+       }
+
+       return 0;
+}
+
 /* DT parsing api for buffer mode */
 static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap)
 {
@@ -920,6 +1016,9 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap)
        u32 temp;
        int rc, i;
 
+       if (hap->wave_rep_cnt > 0 || hap->wave_s_rep_cnt > 0)
+               return 0;
+
        hap->wave_rep_cnt = QPNP_HAP_WAV_REP_MIN;
        rc = of_property_read_u32(pdev->dev.of_node,
                        "qcom,wave-rep-cnt", &temp);
@@ -1251,6 +1350,25 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev,
        return count;
 }
 
+static int parse_string(const char *in_buf, char *out_buf)
+{
+       int i;
+
+       if (snprintf(out_buf, QPNP_HAP_STR_SIZE, "%s", in_buf)
+               > QPNP_HAP_STR_SIZE)
+               return -EINVAL;
+
+       for (i = 0; i < strlen(out_buf); i++) {
+               if (out_buf[i] == ' ' || out_buf[i] == '\n' ||
+                       out_buf[i] == '\t') {
+                       out_buf[i] = '\0';
+                       break;
+               }
+       }
+
+       return 0;
+}
+
 /* 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)
@@ -1259,17 +1377,12 @@ static ssize_t qpnp_hap_play_mode_store(struct device *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;
+       int rc = 0, temp, old_mode;
 
-       if (snprintf(str, QPNP_HAP_STR_SIZE, "%s", buf) > QPNP_HAP_STR_SIZE)
-               return -EINVAL;
+       rc = parse_string(buf, str);
+       if (rc < 0)
+               return rc;
 
-       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)
@@ -1284,10 +1397,10 @@ static ssize_t qpnp_hap_play_mode_store(struct device *dev,
        if (temp == hap->play_mode)
                return count;
 
-       if (temp == QPNP_HAP_BUFFER && !hap->buffer_cfg_state) {
+       if (temp == QPNP_HAP_BUFFER) {
                rc = qpnp_hap_parse_buffer_dt(hap);
                if (!rc)
-                       rc = qpnp_hap_buffer_config(hap);
+                       rc = qpnp_hap_buffer_config(hap, NULL, false);
        } else if (temp == QPNP_HAP_PWM && !hap->pwm_cfg_state) {
                rc = qpnp_hap_parse_pwm_dt(hap);
                if (!rc)
@@ -1436,6 +1549,245 @@ static ssize_t qpnp_hap_ramp_test_data_show(struct device *dev,
 
 }
 
+static ssize_t qpnp_hap_auto_res_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->pmic_subtype == PM660_SUBTYPE) {
+               switch (hap->ares_cfg.auto_res_mode) {
+               case QPNP_HAP_PM660_AUTO_RES_ZXD:
+                       str = "ZXD";
+                       break;
+               case QPNP_HAP_PM660_AUTO_RES_QWD:
+                       str = "QWD";
+                       break;
+               default:
+                       str = "None";
+                       break;
+               }
+       } else {
+               switch (hap->ares_cfg.auto_res_mode) {
+               case QPNP_HAP_AUTO_RES_NONE:
+                       str = "None";
+                       break;
+               case QPNP_HAP_AUTO_RES_ZXD:
+                       str = "ZXD";
+                       break;
+               case QPNP_HAP_AUTO_RES_QWD:
+                       str = "QWD";
+                       break;
+               case QPNP_HAP_AUTO_RES_MAX_QWD:
+                       str = "MAX_QWD";
+                       break;
+               case QPNP_HAP_AUTO_RES_ZXD_EOP:
+                       str = "ZXD_EOP";
+                       break;
+               default:
+                       str = "None";
+                       break;
+               }
+       }
+
+       return snprintf(buf, PAGE_SIZE, "%s\n", str);
+}
+
+static ssize_t qpnp_hap_auto_res_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;
+
+       rc = parse_string(buf, str);
+       if (rc < 0)
+               return rc;
+
+       if (hap->pmic_subtype == PM660_SUBTYPE) {
+               if (strcmp(str, "ZXD") == 0 ||
+                       strcmp(str, "zxd") == 0)
+                       temp = QPNP_HAP_PM660_AUTO_RES_ZXD;
+               else if (strcmp(str, "QWD") == 0 ||
+                               strcmp(str, "qwd") == 0)
+                       temp = QPNP_HAP_PM660_AUTO_RES_QWD;
+               else {
+                       pr_err("Should be ZXD or QWD\n");
+                       return -EINVAL;
+               }
+       } else {
+               if (strcmp(str, "None") == 0)
+                       temp = QPNP_HAP_AUTO_RES_NONE;
+               else if (strcmp(str, "ZXD") == 0 ||
+                               strcmp(str, "zxd") == 0)
+                       temp = QPNP_HAP_AUTO_RES_ZXD;
+               else if (strcmp(str, "QWD") == 0 ||
+                               strcmp(str, "qwd") == 0)
+                       temp = QPNP_HAP_AUTO_RES_QWD;
+               else if (strcmp(str, "ZXD_EOP") == 0 ||
+                               strcmp(str, "zxd_eop") == 0)
+                       temp = QPNP_HAP_AUTO_RES_ZXD_EOP;
+               else if (strcmp(str, "MAX_QWD") == 0 ||
+                               strcmp(str, "max_qwd") == 0)
+                       temp = QPNP_HAP_AUTO_RES_MAX_QWD;
+               else {
+                       pr_err("Should be None or ZXD or QWD or ZXD_EOP or MAX_QWD\n");
+                       return -EINVAL;
+               }
+       }
+
+       hap->ares_cfg.auto_res_mode = temp;
+       return count;
+}
+
+static ssize_t qpnp_hap_hi_z_period_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;
+
+       switch (hap->ares_cfg.lra_high_z) {
+       case QPNP_HAP_LRA_HIGH_Z_NONE:
+               str = "high_z_none";
+               break;
+       case QPNP_HAP_LRA_HIGH_Z_OPT1:
+               str = "high_z_opt1";
+               break;
+       case QPNP_HAP_LRA_HIGH_Z_OPT2:
+               str = "high_z_opt2";
+               break;
+       case QPNP_HAP_LRA_HIGH_Z_OPT3:
+               str = "high_z_opt3";
+               break;
+       }
+
+       return snprintf(buf, PAGE_SIZE, "%s\n", str);
+}
+
+static ssize_t qpnp_hap_hi_z_period_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;
+
+       rc = kstrtoint(buf, 10, &data);
+       if (rc)
+               return rc;
+
+       if (data < QPNP_HAP_LRA_HIGH_Z_NONE
+               || data > QPNP_HAP_LRA_HIGH_Z_OPT3) {
+               pr_err("Invalid high Z configuration\n");
+               return -EINVAL;
+       }
+
+       hap->ares_cfg.lra_high_z = data;
+       return count;
+}
+
+static ssize_t qpnp_hap_calib_period_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->ares_cfg.lra_res_cal_period);
+}
+
+static ssize_t qpnp_hap_calib_period_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;
+
+       rc = kstrtoint(buf, 10, &data);
+       if (rc)
+               return rc;
+
+       if (data < QPNP_HAP_RES_CAL_PERIOD_MIN) {
+               pr_err("Invalid auto resonance calibration period\n");
+               return -EINVAL;
+       }
+
+       if (hap->pmic_subtype == PM660_SUBTYPE) {
+               if (data > QPNP_HAP_PM660_RES_CAL_PERIOD_MAX) {
+                       pr_err("Invalid auto resonance calibration period\n");
+                       return -EINVAL;
+               }
+       } else {
+               if (data > QPNP_HAP_RES_CAL_PERIOD_MAX) {
+                       pr_err("Invalid auto resonance calibration period\n");
+                       return -EINVAL;
+               }
+       }
+
+       hap->ares_cfg.lra_res_cal_period = data;
+       return count;
+}
+
+static ssize_t qpnp_hap_override_auto_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);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", hap->override_auto_mode_config);
+}
+
+static ssize_t qpnp_hap_override_auto_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);
+       int data, rc;
+
+       rc = kstrtoint(buf, 10, &data);
+       if (rc)
+               return rc;
+
+       hap->override_auto_mode_config = data;
+       return count;
+}
+
+static ssize_t qpnp_hap_vmax_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->vmax_mv);
+}
+
+static ssize_t qpnp_hap_vmax_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;
+
+       rc = kstrtoint(buf, 10, &data);
+       if (rc)
+               return rc;
+
+       hap->vmax_mv = data;
+       return count;
+}
+
 /* sysfs attributes */
 static struct device_attribute qpnp_hap_attrs[] = {
        __ATTR(wf_s0, 0664, qpnp_hap_wf_s0_show, qpnp_hap_wf_s0_store),
@@ -1457,6 +1809,16 @@ static struct device_attribute qpnp_hap_attrs[] = {
                qpnp_hap_ramp_test_data_store),
        __ATTR(min_max_test, 0664, qpnp_hap_min_max_test_data_show,
                qpnp_hap_min_max_test_data_store),
+       __ATTR(auto_res_mode, 0664, qpnp_hap_auto_res_mode_show,
+               qpnp_hap_auto_res_mode_store),
+       __ATTR(high_z_period, 0664, qpnp_hap_hi_z_period_show,
+               qpnp_hap_hi_z_period_store),
+       __ATTR(calib_period, 0664, qpnp_hap_calib_period_show,
+               qpnp_hap_calib_period_store),
+       __ATTR(override_auto_mode_config, 0664,
+               qpnp_hap_override_auto_mode_show,
+               qpnp_hap_override_auto_mode_store),
+       __ATTR(vmax_mv, 0664, qpnp_hap_vmax_show, qpnp_hap_vmax_store),
 };
 
 static int calculate_lra_code(struct qpnp_hap *hap)
@@ -1515,9 +1877,47 @@ static int calculate_lra_code(struct qpnp_hap *hap)
 static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
 {
        int rc = 0;
-       u8 val;
+       u32 back_emf_delay_us = hap->time_required_to_generate_back_emf_us;
+       u8 val, auto_res_mode_qwd;
+
+       if (hap->act_type != QPNP_HAP_LRA)
+               return 0;
+
+       if (hap->pmic_subtype == PM660_SUBTYPE)
+               auto_res_mode_qwd = (hap->ares_cfg.auto_res_mode ==
+                                               QPNP_HAP_PM660_AUTO_RES_QWD);
+       else
+               auto_res_mode_qwd = (hap->ares_cfg.auto_res_mode ==
+                                                       QPNP_HAP_AUTO_RES_QWD);
+
+       /*
+        * Do not enable auto resonance if auto mode is enabled and auto
+        * resonance mode is QWD, meaning short pattern.
+        */
+       if (hap->auto_mode && auto_res_mode_qwd && enable) {
+               pr_debug("auto_mode enabled, not enabling auto_res\n");
+               return 0;
+       }
+
+       if (!hap->correct_lra_drive_freq && !auto_res_mode_qwd) {
+               pr_debug("correct_lra_drive_freq: %d auto_res_mode_qwd: %d\n",
+                       hap->correct_lra_drive_freq, auto_res_mode_qwd);
+               return 0;
+       }
 
        val = enable ? AUTO_RES_ENABLE : 0;
+       /*
+        * For auto resonance detection to work properly, sufficient back-emf
+        * has to be generated. In general, back-emf takes some time to build
+        * up. When the auto resonance mode is chosen as QWD, high-z will be
+        * applied for every LRA cycle and hence there won't be enough back-emf
+        * at the start-up. Hence, the motor needs to vibrate for few LRA cycles
+        * after the PLAY bit is asserted. Enable the auto resonance after
+        * 'time_required_to_generate_back_emf_us' is completed.
+        */
+       if (enable)
+               usleep_range(back_emf_delay_us, back_emf_delay_us + 1);
+
        if (hap->pmic_subtype == PM660_SUBTYPE)
                rc = qpnp_hap_masked_write_reg(hap,
                                QPNP_HAP_AUTO_RES_CTRL(hap->base),
@@ -1534,6 +1934,7 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
        else
                hap->status_flags &= ~AUTO_RESONANCE_ENABLED;
 
+       pr_debug("auto_res %sabled\n", enable ? "en" : "dis");
        return rc;
 }
 
@@ -1617,65 +2018,57 @@ static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer)
        return HRTIMER_RESTART;
 }
 
+static bool is_sw_lra_auto_resonance_control(struct qpnp_hap *hap)
+{
+       if (hap->act_type != QPNP_HAP_LRA)
+               return false;
+
+       if (hap->lra_hw_auto_resonance)
+               return false;
+
+       if (!hap->correct_lra_drive_freq)
+               return false;
+
+       if (hap->auto_mode && hap->play_mode == QPNP_HAP_BUFFER)
+               return false;
+
+       return true;
+}
+
 /* set api for haptics */
-static int qpnp_hap_set(struct qpnp_hap *hap, int on)
+static int qpnp_hap_set(struct qpnp_hap *hap, bool on)
 {
-       u8 auto_res_mode_qwd;
        int rc = 0;
        unsigned long timeout_ns = POLL_TIME_AUTO_RES_ERR_NS;
-       u32 back_emf_delay_us = hap->time_required_to_generate_back_emf_us;
 
        if (hap->play_mode == QPNP_HAP_PWM) {
-               if (on)
+               if (on) {
                        rc = pwm_enable(hap->pwm_info.pwm_dev);
-               else
+                       if (rc < 0)
+                               return rc;
+               } else {
                        pwm_disable(hap->pwm_info.pwm_dev);
+               }
        } else if (hap->play_mode == QPNP_HAP_BUFFER ||
                        hap->play_mode == QPNP_HAP_DIRECT) {
                if (on) {
-                       /*
-                        * For auto resonance detection to work properly,
-                        * sufficient back-emf has to be generated. In general,
-                        * back-emf takes some time to build up. When the auto
-                        * resonance mode is chosen as QWD, high-z will be
-                        * applied for every LRA cycle and hence there won't be
-                        * enough back-emf at the start-up. Hence, the motor
-                        * needs to vibrate for few LRA cycles after the PLAY
-                        * bit is asserted. So disable the auto resonance here
-                        * and enable it after the sleep of
-                        * 'time_required_to_generate_back_emf_us' is completed.
-                        */
-                       if (hap->pmic_subtype == PM660_SUBTYPE)
-                               auto_res_mode_qwd = (hap->auto_res_mode ==
-                                               QPNP_HAP_PM660_AUTO_RES_QWD);
-                       else
-                               auto_res_mode_qwd = (hap->auto_res_mode ==
-                                                       QPNP_HAP_AUTO_RES_QWD);
-
-                       if ((hap->act_type == QPNP_HAP_LRA) &&
-                               (hap->correct_lra_drive_freq ||
-                               auto_res_mode_qwd))
-                               qpnp_hap_auto_res_enable(hap, 0);
+                       rc = qpnp_hap_auto_res_enable(hap, 0);
+                       if (rc < 0)
+                               return rc;
 
                        rc = qpnp_hap_mod_enable(hap, on);
                        if (rc < 0)
                                return rc;
 
                        rc = qpnp_hap_play(hap, on);
+                       if (rc < 0)
+                               return rc;
 
-                       if ((hap->act_type == QPNP_HAP_LRA) &&
-                               (hap->correct_lra_drive_freq ||
-                               auto_res_mode_qwd)) {
-                               usleep_range(back_emf_delay_us,
-                                               (back_emf_delay_us + 1));
+                       rc = qpnp_hap_auto_res_enable(hap, 1);
+                       if (rc < 0)
+                               return rc;
 
-                               rc = qpnp_hap_auto_res_enable(hap, 1);
-                               if (rc < 0)
-                                       return rc;
-                       }
-                       if (hap->act_type == QPNP_HAP_LRA &&
-                                       hap->correct_lra_drive_freq &&
-                                       !hap->lra_hw_auto_resonance) {
+                       if (is_sw_lra_auto_resonance_control(hap)) {
                                /*
                                 * Start timer to poll Auto Resonance error bit
                                 */
@@ -1683,7 +2076,7 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
                                hrtimer_cancel(&hap->auto_res_err_poll_timer);
                                hrtimer_start(&hap->auto_res_err_poll_timer,
                                                ktime_set(0, timeout_ns),
-                                                HRTIMER_MODE_REL);
+                                               HRTIMER_MODE_REL);
                                mutex_unlock(&hap->lock);
                        }
                } else {
@@ -1691,54 +2084,182 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
                        if (rc < 0)
                                return rc;
 
-                       if (hap->act_type == QPNP_HAP_LRA &&
-                               hap->correct_lra_drive_freq &&
-                               (hap->status_flags & AUTO_RESONANCE_ENABLED) &&
-                               !hap->lra_hw_auto_resonance) {
+                       if (is_sw_lra_auto_resonance_control(hap) &&
+                               (hap->status_flags & AUTO_RESONANCE_ENABLED))
                                update_lra_frequency(hap);
-                       }
 
                        rc = qpnp_hap_mod_enable(hap, on);
-                       if (hap->act_type == QPNP_HAP_LRA &&
-                                       hap->correct_lra_drive_freq &&
-                                       !hap->lra_hw_auto_resonance) {
+                       if (rc < 0)
+                               return rc;
+
+                       if (is_sw_lra_auto_resonance_control(hap))
                                hrtimer_cancel(&hap->auto_res_err_poll_timer);
-                       }
                }
        }
 
        return rc;
 }
 
+static int qpnp_hap_auto_mode_config(struct qpnp_hap *hap, int time_ms)
+{
+       struct qpnp_hap_lra_ares_cfg ares_cfg;
+       enum qpnp_hap_mode old_play_mode;
+       u8 old_ares_mode;
+       u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN] = {0};
+       u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN] = {0};
+       int rc, vmax_mv;
+
+       /* For now, this is for LRA only */
+       if (hap->act_type == QPNP_HAP_ERM)
+               return 0;
+
+       old_ares_mode = hap->ares_cfg.auto_res_mode;
+       old_play_mode = hap->play_mode;
+       pr_debug("auto_mode, time_ms: %d\n", time_ms);
+       if (time_ms <= 20) {
+               wave_samp[0] = QPNP_HAP_WAV_SAMP_MAX;
+               wave_samp[1] = QPNP_HAP_WAV_SAMP_MAX;
+               if (time_ms > 15)
+                       wave_samp[2] = QPNP_HAP_WAV_SAMP_MAX;
+
+               /* short pattern */
+               rc = qpnp_hap_parse_buffer_dt(hap);
+               if (!rc)
+                       rc = qpnp_hap_buffer_config(hap, wave_samp, true);
+               if (rc < 0) {
+                       pr_err("Error in configuring buffer mode %d\n",
+                               rc);
+                       return rc;
+               }
+
+               ares_cfg.lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1;
+               ares_cfg.lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN;
+               if (hap->pmic_subtype == PM660_SUBTYPE) {
+                       ares_cfg.auto_res_mode =
+                                               QPNP_HAP_PM660_AUTO_RES_QWD;
+                       ares_cfg.lra_qwd_drive_duration = 0;
+                       ares_cfg.calibrate_at_eop = 0;
+               } else {
+                       ares_cfg.auto_res_mode = QPNP_HAP_AUTO_RES_QWD;
+                       ares_cfg.lra_qwd_drive_duration = -EINVAL;
+                       ares_cfg.calibrate_at_eop = -EINVAL;
+               }
+
+               vmax_mv = QPNP_HAP_VMAX_MAX_MV;
+               rc = qpnp_hap_vmax_config(hap, vmax_mv, true);
+               if (rc < 0)
+                       return rc;
+
+               rc = qpnp_hap_brake_config(hap, brake_pat);
+               if (rc < 0)
+                       return rc;
+
+               /* enable play_irq for buffer mode */
+               if (hap->play_irq >= 0 && !hap->play_irq_en) {
+                       enable_irq(hap->play_irq);
+                       hap->play_irq_en = true;
+               }
+
+               hap->play_mode = QPNP_HAP_BUFFER;
+               hap->wave_shape = QPNP_HAP_WAV_SQUARE;
+       } else {
+               /* long pattern */
+               ares_cfg.lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1;
+               if (hap->pmic_subtype == PM660_SUBTYPE) {
+                       ares_cfg.auto_res_mode =
+                               QPNP_HAP_PM660_AUTO_RES_ZXD;
+                       ares_cfg.lra_res_cal_period =
+                               QPNP_HAP_PM660_RES_CAL_PERIOD_MAX;
+                       ares_cfg.lra_qwd_drive_duration = 0;
+                       ares_cfg.calibrate_at_eop = 1;
+               } else {
+                       ares_cfg.auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP;
+                       ares_cfg.lra_res_cal_period =
+                               QPNP_HAP_RES_CAL_PERIOD_MAX;
+                       ares_cfg.lra_qwd_drive_duration = -EINVAL;
+                       ares_cfg.calibrate_at_eop = -EINVAL;
+               }
+
+               vmax_mv = hap->vmax_mv;
+               rc = qpnp_hap_vmax_config(hap, vmax_mv, false);
+               if (rc < 0)
+                       return rc;
+
+               brake_pat[0] = 0x3;
+               rc = qpnp_hap_brake_config(hap, brake_pat);
+               if (rc < 0)
+                       return rc;
+
+               /* enable play_irq for direct mode */
+               if (hap->play_irq >= 0 && hap->play_irq_en) {
+                       disable_irq(hap->play_irq);
+                       hap->play_irq_en = false;
+               }
+
+               hap->play_mode = QPNP_HAP_DIRECT;
+               hap->wave_shape = QPNP_HAP_WAV_SINE;
+       }
+
+       if (hap->override_auto_mode_config) {
+               rc = qpnp_hap_lra_auto_res_config(hap, NULL);
+       } else {
+               hap->ares_cfg.auto_res_mode = ares_cfg.auto_res_mode;
+               rc = qpnp_hap_lra_auto_res_config(hap, &ares_cfg);
+       }
+
+       if (rc < 0) {
+               hap->ares_cfg.auto_res_mode = old_ares_mode;
+               return rc;
+       }
+
+       rc = qpnp_hap_play_mode_config(hap);
+       if (rc < 0) {
+               hap->play_mode = old_play_mode;
+               return rc;
+       }
+
+       rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG2_REG(hap->base),
+                       QPNP_HAP_WAV_SHAPE_MASK, hap->wave_shape);
+       if (rc < 0)
+               return rc;
+
+       return 0;
+}
+
 /* enable interface from timed output class */
-static void qpnp_hap_td_enable(struct timed_output_dev *dev, int value)
+static void qpnp_hap_td_enable(struct timed_output_dev *dev, int time_ms)
 {
        struct qpnp_hap *hap = container_of(dev, struct qpnp_hap,
                                         timed_dev);
+       int rc;
 
-       mutex_lock(&hap->lock);
+       if (time_ms <= 0)
+               return;
 
-       if (hap->act_type == QPNP_HAP_LRA &&
-                               hap->correct_lra_drive_freq &&
-                               !hap->lra_hw_auto_resonance)
+       if (time_ms < 10)
+               time_ms = 10;
+
+       mutex_lock(&hap->lock);
+       if (is_sw_lra_auto_resonance_control(hap))
                hrtimer_cancel(&hap->auto_res_err_poll_timer);
 
        hrtimer_cancel(&hap->hap_timer);
 
-       if (value == 0) {
-               if (hap->state == 0) {
+       if (hap->auto_mode) {
+               rc = qpnp_hap_auto_mode_config(hap, time_ms);
+               if (rc < 0) {
+                       pr_err("Unable to do auto mode config\n");
                        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);
        }
+
+       time_ms = (time_ms > hap->timeout_ms ? hap->timeout_ms : time_ms);
+       hap->play_time_ms = time_ms;
+       hap->state = 1;
+       hrtimer_start(&hap->hap_timer,
+               ktime_set(time_ms / 1000, (time_ms % 1000) * 1000000),
+               HRTIMER_MODE_REL);
        mutex_unlock(&hap->lock);
        schedule_work(&hap->work);
 }
@@ -1824,7 +2345,7 @@ static void qpnp_hap_worker(struct work_struct *work)
        /* Disable haptics module if the duration of short circuit
         * exceeds the maximum limit (5 secs).
         */
-       if (hap->sc_duration == SC_MAX_DURATION) {
+       if (hap->sc_count >= SC_MAX_COUNT) {
                rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base),
                        val);
        } else {
@@ -1890,7 +2411,7 @@ static int qpnp_haptic_suspend(struct device *dev)
        hrtimer_cancel(&hap->hap_timer);
        cancel_work_sync(&hap->work);
        /* turn-off haptic */
-       qpnp_hap_set(hap, 0);
+       qpnp_hap_set(hap, false);
 
        return 0;
 }
@@ -1902,8 +2423,7 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL);
 static int qpnp_hap_config(struct qpnp_hap *hap)
 {
        u8 val = 0;
-       u32 temp;
-       int rc, i;
+       int rc;
 
        /*
         * This denotes the percentage error in rc clock multiplied by 10
@@ -1917,7 +2437,7 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
                return rc;
 
        /* Configure auto resonance parameters */
-       rc = qpnp_hap_lra_auto_res_config(hap);
+       rc = qpnp_hap_lra_auto_res_config(hap, NULL);
        if (rc)
                return rc;
 
@@ -1927,7 +2447,7 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
                return rc;
 
        /* Configure the VMAX register */
-       rc = qpnp_hap_vmax_config(hap);
+       rc = qpnp_hap_vmax_config(hap, hap->vmax_mv, false);
        if (rc)
                return rc;
 
@@ -2037,32 +2557,12 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
                        hap->drive_period_code_min_limit);
        }
 
-       /* Configure BRAKE register */
-       rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EN_CTL2_REG(hap->base),
-                       QPNP_HAP_BRAKE_MASK, (u8)hap->en_brake);
-       if (rc)
-               return rc;
-
-       if (hap->en_brake && hap->sup_brake_pat) {
-               for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, val = 0; i >= 0; i--) {
-                       hap->brake_pat[i] &= QPNP_HAP_BRAKE_PAT_MASK;
-                       temp = i << 1;
-                       val |= hap->brake_pat[i] << temp;
-               }
-               rc = qpnp_hap_write_reg(hap, QPNP_HAP_BRAKE_REG(hap->base),
-                               val);
-               if (rc)
-                       return rc;
-       }
-
-       /* Cache play register */
-       rc = qpnp_hap_read_reg(hap, QPNP_HAP_PLAY_REG(hap->base), &val);
+       rc = qpnp_hap_brake_config(hap, NULL);
        if (rc < 0)
                return rc;
-       hap->reg_play = val;
 
        if (hap->play_mode == QPNP_HAP_BUFFER)
-               rc = qpnp_hap_buffer_config(hap);
+               rc = qpnp_hap_buffer_config(hap, NULL, false);
        else if (hap->play_mode == QPNP_HAP_PWM)
                rc = qpnp_hap_pwm_config(hap);
        else if (hap->play_mode == QPNP_HAP_AUDIO)
@@ -2083,8 +2583,10 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
                }
 
                /* use play_irq only for buffer mode */
-               if (hap->play_mode != QPNP_HAP_BUFFER)
+               if (hap->play_mode != QPNP_HAP_BUFFER) {
                        disable_irq(hap->play_irq);
+                       hap->play_irq_en = false;
+               }
        }
 
        /* setup short circuit irq */
@@ -2099,7 +2601,7 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
                }
        }
 
-       hap->sc_duration = 0;
+       hap->sc_count = 0;
 
        return rc;
 }
@@ -2173,30 +2675,31 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
                                "qcom,lra-auto-res-mode", &temp_str);
                if (!rc) {
                        if (hap->pmic_subtype == PM660_SUBTYPE) {
-                               hap->auto_res_mode =
+                               hap->ares_cfg.auto_res_mode =
                                                QPNP_HAP_PM660_AUTO_RES_QWD;
                                if (strcmp(temp_str, "zxd") == 0)
-                                       hap->auto_res_mode =
+                                       hap->ares_cfg.auto_res_mode =
                                                QPNP_HAP_PM660_AUTO_RES_ZXD;
                                else if (strcmp(temp_str, "qwd") == 0)
-                                       hap->auto_res_mode =
+                                       hap->ares_cfg.auto_res_mode =
                                                QPNP_HAP_PM660_AUTO_RES_QWD;
                        } else {
-                               hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP;
+                               hap->ares_cfg.auto_res_mode =
+                                       QPNP_HAP_AUTO_RES_ZXD_EOP;
                                if (strcmp(temp_str, "none") == 0)
-                                       hap->auto_res_mode =
+                                       hap->ares_cfg.auto_res_mode =
                                                QPNP_HAP_AUTO_RES_NONE;
                                else if (strcmp(temp_str, "zxd") == 0)
-                                       hap->auto_res_mode =
+                                       hap->ares_cfg.auto_res_mode =
                                                QPNP_HAP_AUTO_RES_ZXD;
                                else if (strcmp(temp_str, "qwd") == 0)
-                                       hap->auto_res_mode =
+                                       hap->ares_cfg.auto_res_mode =
                                                QPNP_HAP_AUTO_RES_QWD;
                                else if (strcmp(temp_str, "max-qwd") == 0)
-                                       hap->auto_res_mode =
+                                       hap->ares_cfg.auto_res_mode =
                                                QPNP_HAP_AUTO_RES_MAX_QWD;
                                else
-                                       hap->auto_res_mode =
+                                       hap->ares_cfg.auto_res_mode =
                                                QPNP_HAP_AUTO_RES_ZXD_EOP;
                        }
                } else if (rc != -EINVAL) {
@@ -2204,42 +2707,48 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
                        return rc;
                }
 
-               hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3;
+               hap->ares_cfg.lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3;
                rc = of_property_read_string(pdev->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;
+                               hap->ares_cfg.lra_high_z =
+                                       QPNP_HAP_LRA_HIGH_Z_NONE;
+                       else if (strcmp(temp_str, "opt1") == 0)
+                               hap->ares_cfg.lra_high_z =
+                                       QPNP_HAP_LRA_HIGH_Z_OPT1;
+                       else if (strcmp(temp_str, "opt2") == 0)
+                               hap->ares_cfg.lra_high_z =
+                                        QPNP_HAP_LRA_HIGH_Z_OPT2;
+                       else
+                               hap->ares_cfg.lra_high_z =
+                                        QPNP_HAP_LRA_HIGH_Z_OPT3;
+
                        if (hap->pmic_subtype == PM660_SUBTYPE) {
                                if (strcmp(temp_str, "opt0") == 0)
-                                       hap->lra_high_z =
+                                       hap->ares_cfg.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) {
                        pr_err("Unable to read LRA high-z\n");
                        return rc;
                }
 
-               hap->lra_qwd_drive_duration = -EINVAL;
+               hap->ares_cfg.lra_qwd_drive_duration = -EINVAL;
                rc = of_property_read_u32(pdev->dev.of_node,
                                "qcom,lra-qwd-drive-duration",
-                               &hap->lra_qwd_drive_duration);
+                               &hap->ares_cfg.lra_qwd_drive_duration);
 
-               hap->calibrate_at_eop = -EINVAL;
+               hap->ares_cfg.calibrate_at_eop = -EINVAL;
                rc = of_property_read_u32(pdev->dev.of_node,
-                       "qcom,lra-calibrate-at-eop", &hap->calibrate_at_eop);
+                               "qcom,lra-calibrate-at-eop",
+                               &hap->ares_cfg.calibrate_at_eop);
 
-               hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX;
+               hap->ares_cfg.lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX;
                rc = of_property_read_u32(pdev->dev.of_node,
                                "qcom,lra-res-cal-period", &temp);
                if (!rc) {
-                       hap->lra_res_cal_period = temp;
+                       hap->ares_cfg.lra_res_cal_period = temp;
                } else if (rc != -EINVAL) {
                        pr_err("Unable to read cal period\n");
                        return rc;
@@ -2271,7 +2780,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
                        hap->drive_period_code_min_limit_percent_variation =
                                                                (u8) temp;
 
-               if (hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) {
+               if (hap->ares_cfg.auto_res_mode == QPNP_HAP_AUTO_RES_QWD) {
                        hap->time_required_to_generate_back_emf_us =
                                        QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN;
                        rc = of_property_read_u32(pdev->dev.of_node,
@@ -2409,6 +2918,8 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
        if (of_find_property(pdev->dev.of_node, "vcc_pon-supply", NULL))
                hap->manage_pon_supply = true;
 
+       hap->auto_mode = of_property_read_bool(pdev->dev.of_node,
+                               "qcom,lra-auto-mode");
        return 0;
 }
 
@@ -2503,12 +3014,9 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
        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 &&
-                                               !hap->lra_hw_auto_resonance) {
-               hrtimer_init(&hap->auto_res_err_poll_timer, CLOCK_MONOTONIC,
-                                               HRTIMER_MODE_REL);
-               hap->auto_res_err_poll_timer.function = detect_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) {
@@ -2546,9 +3054,7 @@ sysfs_fail:
        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 &&
-                                               !hap->lra_hw_auto_resonance)
-               hrtimer_cancel(&hap->auto_res_err_poll_timer);
+       hrtimer_cancel(&hap->auto_res_err_poll_timer);
        hrtimer_cancel(&hap->hap_timer);
        mutex_destroy(&hap->lock);
        mutex_destroy(&hap->wf_lock);
@@ -2566,9 +3072,7 @@ static int qpnp_haptic_remove(struct platform_device *pdev)
                                &qpnp_hap_attrs[i].attr);
 
        cancel_work_sync(&hap->work);
-       if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq &&
-                                               !hap->lra_hw_auto_resonance)
-               hrtimer_cancel(&hap->auto_res_err_poll_timer);
+       hrtimer_cancel(&hap->auto_res_err_poll_timer);
        hrtimer_cancel(&hap->hap_timer);
        timed_output_dev_unregister(&hap->timed_dev);
        mutex_destroy(&hap->lock);