msm: qpnp-haptic: Add support for haptics on PM660

Update the driver to support PM660. PM660 add dedicated
register for auto-resonance control and a few other changes
to haptics configuration.

CRs-Fixed: 2016588
Change-Id: Ia9d65bc0a1169b5cba1c122d50d49c8102ac79f5
Signed-off-by: Ankit Sharma <ansharma@codeaurora.org>
This commit is contained in:
Ankit Sharma 2017-03-07 18:31:23 +05:30
parent 1450b9c7da
commit 3dde7b3e01
4 changed files with 222 additions and 56 deletions

View file

@ -10,6 +10,7 @@ pwm(pulse width modulation) and audio.
Required Properties: Required Properties:
- compatible: must be "qcom,qpnp-haptic" - compatible: must be "qcom,qpnp-haptic"
- reg: address of device - reg: address of device
- qcom,pmic-revid : phandle to fetch PMIC revid
- qcom,actuator-type: must be one of "erm" or "lra" - qcom,actuator-type: must be one of "erm" or "lra"
- qcom,play-mode : must be one of "buffer", "direct", "pwm" or "audio" - qcom,play-mode : must be one of "buffer", "direct", "pwm" or "audio"
@ -63,7 +64,13 @@ Optional properties when qcom,actuator-type is "lra"
"max-qwd" : Maximum QWD "max-qwd" : Maximum QWD
"zxd-eop" : ZXD + End of pattern (This is the Default) "zxd-eop" : ZXD + End of pattern (This is the Default)
- qcom,lra-high-z : High Z configuration for auto resonance. Possible string values are - qcom,lra-high-z : High Z configuration for auto resonance. Possible string values are
"none", "opt1", "opt2" and "opt3" (default) "none", "opt1", "opt2" and "opt3" (default). For PM660,
"opt0" is valid value for 1 LRA period.
- qcom,lra-qwd-drive-duration : Drive duration of LRA in QWD mode for PM660.
Possible values are: 0: 1/4 LRA PERIOD and 1: 3/8 LRA PERIOD
- qcom,lra-calibrate-at-eop : To calibrate at End of Pattern for PM660.
Possible values are: 0 to disable and 1 to enable Calibration
at End of Pattern
- qcom,lra-res-cal-period : Auto resonance calibration period. The values range from - qcom,lra-res-cal-period : Auto resonance calibration period. The values range from
4 to 32(default) 4 to 32(default)
- qcom,perform-lra-auto-resonance-search : boolean, define this property if: - qcom,perform-lra-auto-resonance-search : boolean, define this property if:
@ -109,6 +116,7 @@ Example:
interrupts = <0x3 0xc0 0x0>, interrupts = <0x3 0xc0 0x0>,
<0x3 0xc0 0x1>; <0x3 0xc0 0x1>;
interrupt-names = "sc-irq", "play-irq"; interrupt-names = "sc-irq", "play-irq";
qcom,pmic-revid = <&pm660_revid>;
vcc_pon-supply = <&pon_perph_reg>; vcc_pon-supply = <&pon_perph_reg>;
qcom,play-mode = "direct"; qcom,play-mode = "direct";
qcom,wave-play-rate-us = <5263>; qcom,wave-play-rate-us = <5263>;

View file

@ -607,6 +607,7 @@
interrupts = <0x1 0xc0 0x0 IRQ_TYPE_NONE>, interrupts = <0x1 0xc0 0x0 IRQ_TYPE_NONE>,
<0x1 0xc0 0x1 IRQ_TYPE_NONE>; <0x1 0xc0 0x1 IRQ_TYPE_NONE>;
interrupt-names = "sc-irq", "play-irq"; interrupt-names = "sc-irq", "play-irq";
qcom,pmic-revid = <&pm660_revid>;
qcom,actuator-type = "lra"; qcom,actuator-type = "lra";
qcom,play-mode = "direct"; qcom,play-mode = "direct";
qcom,vmax-mv = <3200>; qcom,vmax-mv = <3200>;
@ -619,9 +620,9 @@
qcom,brake-pattern = [03 03 00 00]; qcom,brake-pattern = [03 03 00 00];
qcom,use-play-irq; qcom,use-play-irq;
qcom,use-sc-irq; qcom,use-sc-irq;
qcom,lra-high-z = "opt1"; qcom,lra-high-z = "opt0";
qcom,lra-auto-res-mode = "qwd"; qcom,lra-auto-res-mode = "qwd";
qcom,lra-res-cal-period = <4>; qcom,lra-calibrate-at-eop = <0>;
qcom,correct-lra-drive-freq; qcom,correct-lra-drive-freq;
qcom,misc-trim-error-rc19p2-clk-reg-present; qcom,misc-trim-error-rc19p2-clk-reg-present;
}; };

View file

@ -631,6 +631,7 @@
interrupts = <0x3 0xc0 0x0 IRQ_TYPE_NONE>, interrupts = <0x3 0xc0 0x0 IRQ_TYPE_NONE>,
<0x3 0xc0 0x1 IRQ_TYPE_NONE>; <0x3 0xc0 0x1 IRQ_TYPE_NONE>;
interrupt-names = "sc-irq", "play-irq"; interrupt-names = "sc-irq", "play-irq";
qcom,pmic-revid = <&pmi8998_revid>;
qcom,actuator-type = "lra"; qcom,actuator-type = "lra";
qcom,play-mode = "direct"; qcom,play-mode = "direct";
qcom,vmax-mv = <3200>; qcom,vmax-mv = <3200>;

View file

@ -25,6 +25,7 @@
#include <linux/qpnp/pwm.h> #include <linux/qpnp/pwm.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/qpnp/qpnp-revid.h>
#include <linux/qpnp/qpnp-haptic.h> #include <linux/qpnp/qpnp-haptic.h>
#include "../../staging/android/timed_output.h" #include "../../staging/android/timed_output.h"
@ -37,6 +38,7 @@
#define QPNP_HAP_LRA_AUTO_RES_HI(b) (b + 0x0C) #define QPNP_HAP_LRA_AUTO_RES_HI(b) (b + 0x0C)
#define QPNP_HAP_EN_CTL_REG(b) (b + 0x46) #define QPNP_HAP_EN_CTL_REG(b) (b + 0x46)
#define QPNP_HAP_EN_CTL2_REG(b) (b + 0x48) #define QPNP_HAP_EN_CTL2_REG(b) (b + 0x48)
#define QPNP_HAP_AUTO_RES_CTRL(b) (b + 0x4B)
#define QPNP_HAP_ACT_TYPE_REG(b) (b + 0x4C) #define QPNP_HAP_ACT_TYPE_REG(b) (b + 0x4C)
#define QPNP_HAP_WAV_SHAPE_REG(b) (b + 0x4D) #define QPNP_HAP_WAV_SHAPE_REG(b) (b + 0x4D)
#define QPNP_HAP_PLAY_MODE_REG(b) (b + 0x4E) #define QPNP_HAP_PLAY_MODE_REG(b) (b + 0x4E)
@ -62,13 +64,25 @@
#define QPNP_HAP_ACT_TYPE_MASK 0xFE #define QPNP_HAP_ACT_TYPE_MASK 0xFE
#define QPNP_HAP_LRA 0x0 #define QPNP_HAP_LRA 0x0
#define QPNP_HAP_ERM 0x1 #define QPNP_HAP_ERM 0x1
#define QPNP_HAP_AUTO_RES_MODE_MASK 0x8F #define QPNP_HAP_AUTO_RES_MODE_MASK GENMASK(6, 4)
#define QPNP_HAP_AUTO_RES_MODE_SHIFT 4 #define QPNP_HAP_AUTO_RES_MODE_SHIFT 4
#define QPNP_HAP_LRA_HIGH_Z_MASK 0xF3 #define QPNP_HAP_PM660_AUTO_RES_MODE_BIT BIT(7)
#define QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT 7
#define QPNP_HAP_PM660_CALIBRATE_DURATION_MASK GENMASK(6, 5)
#define QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT 5
#define QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT BIT(4)
#define QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT 4
#define QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT BIT(3)
#define QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT 3
#define QPNP_HAP_PM660_LRA_ZXD_CAL_PERIOD_BIT GENMASK(2, 0)
#define QPNP_HAP_LRA_HIGH_Z_MASK GENMASK(3, 2)
#define QPNP_HAP_LRA_HIGH_Z_SHIFT 2 #define QPNP_HAP_LRA_HIGH_Z_SHIFT 2
#define QPNP_HAP_LRA_RES_CAL_PER_MASK 0xFC #define QPNP_HAP_LRA_RES_CAL_PER_MASK GENMASK(1, 0)
#define QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK GENMASK(2, 0)
#define QPNP_HAP_RES_CAL_PERIOD_MIN 4 #define QPNP_HAP_RES_CAL_PERIOD_MIN 4
#define QPNP_HAP_RES_CAL_PERIOD_MAX 32 #define QPNP_HAP_RES_CAL_PERIOD_MAX 32
#define QPNP_HAP_PM660_RES_CAL_PERIOD_MIN 4
#define QPNP_HAP_PM660_RES_CAL_PERIOD_MAX 256
#define QPNP_HAP_PLAY_MODE_MASK 0xCF #define QPNP_HAP_PLAY_MODE_MASK 0xCF
#define QPNP_HAP_PLAY_MODE_SHFT 4 #define QPNP_HAP_PLAY_MODE_SHFT 4
#define QPNP_HAP_VMAX_MASK 0xC1 #define QPNP_HAP_VMAX_MASK 0xC1
@ -130,7 +144,6 @@
#define QPNP_HAP_TEST2_AUTO_RES_MASK 0x7F #define QPNP_HAP_TEST2_AUTO_RES_MASK 0x7F
#define QPNP_HAP_SEC_UNLOCK 0xA5 #define QPNP_HAP_SEC_UNLOCK 0xA5
#define AUTO_RES_ENABLE 0x80 #define AUTO_RES_ENABLE 0x80
#define AUTO_RES_DISABLE 0x00
#define AUTO_RES_ERR_BIT 0x10 #define AUTO_RES_ERR_BIT 0x10
#define SC_FOUND_BIT 0x08 #define SC_FOUND_BIT 0x08
#define SC_MAX_DURATION 5 #define SC_MAX_DURATION 5
@ -222,9 +235,14 @@ enum qpnp_hap_auto_res_mode {
QPNP_HAP_AUTO_RES_ZXD_EOP, QPNP_HAP_AUTO_RES_ZXD_EOP,
}; };
enum qpnp_hap_pm660_auto_res_mode {
QPNP_HAP_PM660_AUTO_RES_ZXD,
QPNP_HAP_PM660_AUTO_RES_QWD,
};
/* high Z option lines */ /* high Z option lines */
enum qpnp_hap_high_z { enum qpnp_hap_high_z {
QPNP_HAP_LRA_HIGH_Z_NONE, QPNP_HAP_LRA_HIGH_Z_NONE, /* opt0 for PM660 */
QPNP_HAP_LRA_HIGH_Z_OPT1, QPNP_HAP_LRA_HIGH_Z_OPT1,
QPNP_HAP_LRA_HIGH_Z_OPT2, QPNP_HAP_LRA_HIGH_Z_OPT2,
QPNP_HAP_LRA_HIGH_Z_OPT3, QPNP_HAP_LRA_HIGH_Z_OPT3,
@ -240,7 +258,7 @@ enum qpnp_hap_mode {
/* status flags */ /* status flags */
enum qpnp_hap_status { enum qpnp_hap_status {
AUTO_RESONANCE_ENABLED = 1, AUTO_RESONANCE_ENABLED = BIT(0),
}; };
/* pwm channel info */ /* pwm channel info */
@ -271,6 +289,7 @@ struct qpnp_pwm_info {
percentage variation on the higher side. percentage variation on the higher side.
* @ drive_period_code_min_limit - calculated drive period code with * @ drive_period_code_min_limit - calculated drive period code with
percentage variation on the lower side percentage variation on the lower side
* @ lra_res_cal_period - LRA resonance calibration period
* @ play_mode - play mode * @ play_mode - play mode
* @ auto_res_mode - auto resonace mode * @ auto_res_mode - auto resonace mode
* @ lra_high_z - high z option line * @ lra_high_z - high z option line
@ -327,8 +346,9 @@ struct qpnp_hap {
struct mutex wf_lock; struct mutex wf_lock;
struct completion completion; struct completion completion;
enum qpnp_hap_mode play_mode; enum qpnp_hap_mode play_mode;
enum qpnp_hap_auto_res_mode auto_res_mode;
enum qpnp_hap_high_z lra_high_z; enum qpnp_hap_high_z lra_high_z;
int lra_qwd_drive_duration;
int calibrate_at_eop;
u32 init_drive_period_code; u32 init_drive_period_code;
u32 timeout_ms; u32 timeout_ms;
u32 time_required_to_generate_back_emf_us; u32 time_required_to_generate_back_emf_us;
@ -346,6 +366,7 @@ struct qpnp_hap {
u16 base; u16 base;
u16 drive_period_code_max_limit; u16 drive_period_code_max_limit;
u16 drive_period_code_min_limit; u16 drive_period_code_min_limit;
u16 lra_res_cal_period;
u8 drive_period_code_max_limit_percent_variation; u8 drive_period_code_max_limit_percent_variation;
u8 drive_period_code_min_limit_percent_variation; u8 drive_period_code_min_limit_percent_variation;
u8 act_type; u8 act_type;
@ -355,9 +376,10 @@ struct qpnp_hap {
u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN]; u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN];
u8 reg_en_ctl; u8 reg_en_ctl;
u8 reg_play; u8 reg_play;
u8 lra_res_cal_period;
u8 sc_duration; u8 sc_duration;
u8 ext_pwm_dtest_line; u8 ext_pwm_dtest_line;
u8 pmic_subtype;
u8 auto_res_mode;
bool vcc_pon_enabled; bool vcc_pon_enabled;
bool state; bool state;
bool use_play_irq; bool use_play_irq;
@ -403,6 +425,21 @@ static int qpnp_hap_write_reg(struct qpnp_hap *hap, u8 *data, u16 addr)
return rc; return rc;
} }
static int
qpnp_hap_masked_write_reg(struct qpnp_hap *hap, u8 val, u16 addr, u8 mask)
{
int rc;
rc = regmap_update_bits(hap->regmap, addr, mask, val);
if (rc < 0)
pr_err("Unable to update bits from 0x%04X, rc = %d\n", addr,
rc);
else
pr_debug("Wrote 0x%02X to addr 0x%04X\n", val, addr);
return rc;
}
/* helper to access secure registers */ /* helper to access secure registers */
static int qpnp_hap_sec_access(struct qpnp_hap *hap) static int qpnp_hap_sec_access(struct qpnp_hap *hap)
{ {
@ -1413,24 +1450,24 @@ static int calculate_lra_code(struct qpnp_hap *hap)
static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable)
{ {
int rc = 0; int rc = 0;
u8 val; u8 val = 0;
u16 addr;
rc = qpnp_hap_read_reg(hap, &val, QPNP_HAP_TEST2_REG(hap->base));
if (rc < 0) if (hap->pmic_subtype == PM660_SUBTYPE) {
return rc; addr = QPNP_HAP_AUTO_RES_CTRL(hap->base);
val &= QPNP_HAP_TEST2_AUTO_RES_MASK; } else {
addr = QPNP_HAP_TEST2_REG(hap->base);
/* TEST2 is a secure access register */
rc = qpnp_hap_sec_access(hap);
if (rc)
return rc;
}
if (enable) if (enable)
val |= AUTO_RES_ENABLE; val |= AUTO_RES_ENABLE;
else
val |= AUTO_RES_DISABLE;
/* TEST2 is a secure access register */ rc = qpnp_hap_masked_write_reg(hap, val, addr, AUTO_RES_ENABLE);
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) if (rc)
return rc; return rc;
@ -1512,6 +1549,7 @@ static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer)
/* set api for haptics */ /* set api for haptics */
static int qpnp_hap_set(struct qpnp_hap *hap, int on) static int qpnp_hap_set(struct qpnp_hap *hap, int on)
{ {
u8 auto_res_mode_qwd;
int rc = 0; int rc = 0;
unsigned long timeout_ns = POLL_TIME_AUTO_RES_ERR_NS; unsigned long timeout_ns = POLL_TIME_AUTO_RES_ERR_NS;
u32 back_emf_delay_us = hap->time_required_to_generate_back_emf_us; u32 back_emf_delay_us = hap->time_required_to_generate_back_emf_us;
@ -1536,9 +1574,16 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
* and enable it after the sleep of * and enable it after the sleep of
* 'time_required_to_generate_back_emf_us' is completed. * '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) && if ((hap->act_type == QPNP_HAP_LRA) &&
(hap->correct_lra_drive_freq || (hap->correct_lra_drive_freq ||
hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)) auto_res_mode_qwd))
qpnp_hap_auto_res_enable(hap, 0); qpnp_hap_auto_res_enable(hap, 0);
rc = qpnp_hap_mod_enable(hap, on); rc = qpnp_hap_mod_enable(hap, on);
@ -1549,7 +1594,7 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on)
if ((hap->act_type == QPNP_HAP_LRA) && if ((hap->act_type == QPNP_HAP_LRA) &&
(hap->correct_lra_drive_freq || (hap->correct_lra_drive_freq ||
hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)) { auto_res_mode_qwd)) {
usleep_range(back_emf_delay_us, usleep_range(back_emf_delay_us,
(back_emf_delay_us + 1)); (back_emf_delay_us + 1));
@ -1782,7 +1827,7 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL);
/* Configuration api for haptics registers */ /* Configuration api for haptics registers */
static int qpnp_hap_config(struct qpnp_hap *hap) static int qpnp_hap_config(struct qpnp_hap *hap)
{ {
u8 reg = 0, unlock_val; u8 reg = 0, unlock_val, mask;
u32 temp; u32 temp;
int rc, i; int rc, i;
uint error_code = 0; uint error_code = 0;
@ -1804,24 +1849,66 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
/* Configure auto resonance parameters */ /* Configure auto resonance parameters */
if (hap->act_type == QPNP_HAP_LRA) { if (hap->act_type == QPNP_HAP_LRA) {
if (hap->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN) if (hap->pmic_subtype == PM660_SUBTYPE) {
hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN; if (hap->lra_res_cal_period <
else if (hap->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX) QPNP_HAP_PM660_RES_CAL_PERIOD_MIN)
hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; hap->lra_res_cal_period =
QPNP_HAP_PM660_RES_CAL_PERIOD_MIN;
else if (hap->lra_res_cal_period >
QPNP_HAP_PM660_RES_CAL_PERIOD_MAX)
hap->lra_res_cal_period =
QPNP_HAP_PM660_RES_CAL_PERIOD_MAX;
} else if (hap->pmic_subtype != PM660_SUBTYPE) {
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;
}
if (hap->pmic_subtype == PM660_SUBTYPE &&
hap->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD) {
hap->lra_res_cal_period = 0;
}
rc = qpnp_hap_read_reg(hap, &reg, reg = mask = 0;
QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); if (hap->pmic_subtype == PM660_SUBTYPE) {
if (rc < 0) reg |= hap->auto_res_mode <<
return rc; QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT;
reg &= QPNP_HAP_AUTO_RES_MODE_MASK; mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT;
reg |= (hap->auto_res_mode << QPNP_HAP_AUTO_RES_MODE_SHIFT); reg |= hap->lra_high_z <<
reg &= QPNP_HAP_LRA_HIGH_Z_MASK; QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT;
reg |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT); mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK;
reg &= QPNP_HAP_LRA_RES_CAL_PER_MASK; if (hap->lra_qwd_drive_duration != -EINVAL) {
temp = fls(hap->lra_res_cal_period) - 1; reg |= hap->lra_qwd_drive_duration <<
reg |= (temp - 2); QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT;
rc = qpnp_hap_write_reg(hap, &reg, mask |= QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT;
QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); }
if (hap->calibrate_at_eop != -EINVAL) {
reg |= hap->calibrate_at_eop <<
QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT;
mask |= QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT;
}
if (hap->lra_res_cal_period) {
temp = fls(hap->lra_res_cal_period) - 1;
reg |= (temp - 1);
}
mask |= QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK;
} else {
reg |= (hap->auto_res_mode <<
QPNP_HAP_AUTO_RES_MODE_SHIFT);
mask = QPNP_HAP_AUTO_RES_MODE_MASK;
reg |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT);
mask |= QPNP_HAP_LRA_HIGH_Z_MASK;
temp = fls(hap->lra_res_cal_period) - 1;
reg |= (temp - 2);
mask |= QPNP_HAP_LRA_RES_CAL_PER_MASK;
}
rc = qpnp_hap_masked_write_reg(hap, reg,
QPNP_HAP_LRA_AUTO_RES_REG(hap->base),
mask);
if (rc) if (rc)
return rc; return rc;
} else { } else {
@ -1867,8 +1954,13 @@ static int qpnp_hap_config(struct qpnp_hap *hap)
/* Configure the INTERNAL_PWM register */ /* Configure the INTERNAL_PWM register */
if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) { 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; if (hap->pmic_subtype == PM660_SUBTYPE) {
temp = 0; hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ;
temp = 1;
} else {
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) { } 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; hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ;
temp = 1; temp = 1;
@ -2108,20 +2200,36 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
} }
if (hap->act_type == QPNP_HAP_LRA) { if (hap->act_type == QPNP_HAP_LRA) {
hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP;
rc = of_property_read_string(pdev->dev.of_node, rc = of_property_read_string(pdev->dev.of_node,
"qcom,lra-auto-res-mode", &temp_str); "qcom,lra-auto-res-mode", &temp_str);
if (!rc) { if (!rc) {
if (strcmp(temp_str, "none") == 0) if (hap->pmic_subtype == PM660_SUBTYPE) {
hap->auto_res_mode = QPNP_HAP_AUTO_RES_NONE; hap->auto_res_mode =
else if (strcmp(temp_str, "zxd") == 0) QPNP_HAP_PM660_AUTO_RES_QWD;
hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD; if (strcmp(temp_str, "zxd") == 0)
else if (strcmp(temp_str, "qwd") == 0) hap->auto_res_mode =
hap->auto_res_mode = QPNP_HAP_AUTO_RES_QWD; QPNP_HAP_PM660_AUTO_RES_ZXD;
else if (strcmp(temp_str, "max-qwd") == 0) else if (strcmp(temp_str, "qwd") == 0)
hap->auto_res_mode = QPNP_HAP_AUTO_RES_MAX_QWD; hap->auto_res_mode =
else QPNP_HAP_PM660_AUTO_RES_QWD;
} else {
hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP;
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) { } else if (rc != -EINVAL) {
dev_err(&pdev->dev, "Unable to read auto res mode\n"); dev_err(&pdev->dev, "Unable to read auto res mode\n");
return rc; return rc;
@ -2133,6 +2241,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
if (!rc) { if (!rc) {
if (strcmp(temp_str, "none") == 0) if (strcmp(temp_str, "none") == 0)
hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_NONE; hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_NONE;
if (hap->pmic_subtype == PM660_SUBTYPE) {
if (strcmp(temp_str, "opt0") == 0)
hap->lra_high_z =
QPNP_HAP_LRA_HIGH_Z_NONE;
}
else if (strcmp(temp_str, "opt1") == 0) else if (strcmp(temp_str, "opt1") == 0)
hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1; hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1;
else if (strcmp(temp_str, "opt2") == 0) else if (strcmp(temp_str, "opt2") == 0)
@ -2144,6 +2257,15 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
return rc; return rc;
} }
hap->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->calibrate_at_eop = -EINVAL;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,lra-calibrate-at-eop", &hap->calibrate_at_eop);
hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX;
rc = of_property_read_u32(pdev->dev.of_node, rc = of_property_read_u32(pdev->dev.of_node,
"qcom,lra-res-cal-period", &temp); "qcom,lra-res-cal-period", &temp);
@ -2321,6 +2443,34 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap)
return 0; return 0;
} }
static int qpnp_hap_get_pmic_revid(struct qpnp_hap *hap)
{
struct pmic_revid_data *pmic_rev_id;
struct device_node *revid_dev_node;
revid_dev_node = of_parse_phandle(hap->pdev->dev.of_node,
"qcom,pmic-revid", 0);
if (!revid_dev_node) {
pr_err("Missing qcom,pmic-revid property - driver failed\n");
return -EINVAL;
}
pmic_rev_id = get_revid_data(revid_dev_node);
if (IS_ERR_OR_NULL(pmic_rev_id)) {
pr_err("Unable to get pmic_revid rc=%ld\n",
PTR_ERR(pmic_rev_id));
/*
* the revid peripheral must be registered, any failure
* here only indicates that the rev-id module has not
* probed yet.
*/
return -EPROBE_DEFER;
}
hap->pmic_subtype = pmic_rev_id->pmic_subtype;
return 0;
}
static int qpnp_haptic_probe(struct platform_device *pdev) static int qpnp_haptic_probe(struct platform_device *pdev)
{ {
struct qpnp_hap *hap; struct qpnp_hap *hap;
@ -2350,6 +2500,12 @@ static int qpnp_haptic_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, hap); dev_set_drvdata(&pdev->dev, hap);
rc = qpnp_hap_get_pmic_revid(hap);
if (rc) {
pr_err("Unable to check PMIC version rc=%d\n", rc);
return rc;
}
rc = qpnp_hap_parse_dt(hap); rc = qpnp_hap_parse_dt(hap);
if (rc) { if (rc) {
dev_err(&pdev->dev, "DT parsing failed\n"); dev_err(&pdev->dev, "DT parsing failed\n");