Loading Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt +9 −1 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ pwm(pulse width modulation) and audio. Required Properties: - compatible: must be "qcom,qpnp-haptic" - reg: address of device - qcom,pmic-revid : phandle to fetch PMIC revid - qcom,actuator-type: must be one of "erm" or "lra" - qcom,play-mode : must be one of "buffer", "direct", "pwm" or "audio" Loading Loading @@ -63,7 +64,13 @@ Optional properties when qcom,actuator-type is "lra" "max-qwd" : Maximum QWD "zxd-eop" : ZXD + End of pattern (This is the Default) - 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 4 to 32(default) - qcom,perform-lra-auto-resonance-search : boolean, define this property if: Loading Loading @@ -109,6 +116,7 @@ Example: interrupts = <0x3 0xc0 0x0>, <0x3 0xc0 0x1>; interrupt-names = "sc-irq", "play-irq"; qcom,pmic-revid = <&pm660_revid>; vcc_pon-supply = <&pon_perph_reg>; qcom,play-mode = "direct"; qcom,wave-play-rate-us = <5263>; Loading arch/arm/boot/dts/qcom/msm-pm660.dtsi +3 −2 Original line number Diff line number Diff line Loading @@ -607,6 +607,7 @@ interrupts = <0x1 0xc0 0x0 IRQ_TYPE_NONE>, <0x1 0xc0 0x1 IRQ_TYPE_NONE>; interrupt-names = "sc-irq", "play-irq"; qcom,pmic-revid = <&pm660_revid>; qcom,actuator-type = "lra"; qcom,play-mode = "direct"; qcom,vmax-mv = <3200>; Loading @@ -619,9 +620,9 @@ qcom,brake-pattern = [03 03 00 00]; qcom,use-play-irq; qcom,use-sc-irq; qcom,lra-high-z = "opt1"; qcom,lra-high-z = "opt0"; qcom,lra-auto-res-mode = "qwd"; qcom,lra-res-cal-period = <4>; qcom,lra-calibrate-at-eop = <0>; qcom,correct-lra-drive-freq; qcom,misc-trim-error-rc19p2-clk-reg-present; }; Loading arch/arm/boot/dts/qcom/msm-pmi8998.dtsi +1 −0 Original line number Diff line number Diff line Loading @@ -631,6 +631,7 @@ interrupts = <0x3 0xc0 0x0 IRQ_TYPE_NONE>, <0x3 0xc0 0x1 IRQ_TYPE_NONE>; interrupt-names = "sc-irq", "play-irq"; qcom,pmic-revid = <&pmi8998_revid>; qcom,actuator-type = "lra"; qcom,play-mode = "direct"; qcom,vmax-mv = <3200>; Loading drivers/soc/qcom/qpnp-haptic.c +209 −53 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <linux/qpnp/pwm.h> #include <linux/err.h> #include <linux/delay.h> #include <linux/qpnp/qpnp-revid.h> #include <linux/qpnp/qpnp-haptic.h> #include "../../staging/android/timed_output.h" Loading @@ -37,6 +38,7 @@ #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_AUTO_RES_CTRL(b) (b + 0x4B) #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) Loading @@ -62,13 +64,25 @@ #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_MASK GENMASK(6, 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_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_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_SHFT 4 #define QPNP_HAP_VMAX_MASK 0xC1 Loading Loading @@ -130,7 +144,6 @@ #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 Loading Loading @@ -222,9 +235,14 @@ enum qpnp_hap_auto_res_mode { 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 */ 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_OPT2, QPNP_HAP_LRA_HIGH_Z_OPT3, Loading @@ -240,7 +258,7 @@ enum qpnp_hap_mode { /* status flags */ enum qpnp_hap_status { AUTO_RESONANCE_ENABLED = 1, AUTO_RESONANCE_ENABLED = BIT(0), }; /* pwm channel info */ Loading Loading @@ -271,6 +289,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 Loading Loading @@ -327,8 +346,9 @@ struct qpnp_hap { 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; int lra_qwd_drive_duration; int calibrate_at_eop; u32 init_drive_period_code; u32 timeout_ms; u32 time_required_to_generate_back_emf_us; Loading @@ -346,6 +366,7 @@ struct qpnp_hap { u16 base; 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; Loading @@ -355,9 +376,10 @@ struct qpnp_hap { 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; u8 pmic_subtype; u8 auto_res_mode; bool vcc_pon_enabled; bool state; bool use_play_irq; Loading Loading @@ -403,6 +425,21 @@ static int qpnp_hap_write_reg(struct qpnp_hap *hap, u8 *data, u16 addr) 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 */ static int qpnp_hap_sec_access(struct qpnp_hap *hap) { Loading Loading @@ -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) { 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; u8 val = 0; u16 addr; if (enable) val |= AUTO_RES_ENABLE; else val |= AUTO_RES_DISABLE; if (hap->pmic_subtype == PM660_SUBTYPE) { addr = QPNP_HAP_AUTO_RES_CTRL(hap->base); } else { addr = QPNP_HAP_TEST2_REG(hap->base); /* 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 (enable) val |= AUTO_RES_ENABLE; rc = qpnp_hap_masked_write_reg(hap, val, addr, AUTO_RES_ENABLE); if (rc) return rc; Loading Loading @@ -1512,6 +1549,7 @@ static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) /* set api for haptics */ static int qpnp_hap_set(struct qpnp_hap *hap, int 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; Loading @@ -1536,9 +1574,16 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) * 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 || hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)) auto_res_mode_qwd)) qpnp_hap_auto_res_enable(hap, 0); rc = qpnp_hap_mod_enable(hap, on); Loading @@ -1549,7 +1594,7 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) if ((hap->act_type == QPNP_HAP_LRA) && (hap->correct_lra_drive_freq || hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)) { auto_res_mode_qwd)) { usleep_range(back_emf_delay_us, (back_emf_delay_us + 1)); Loading Loading @@ -1782,7 +1827,7 @@ 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, unlock_val; u8 reg = 0, unlock_val, mask; u32 temp; int rc, i; uint error_code = 0; Loading @@ -1804,24 +1849,66 @@ static int qpnp_hap_config(struct qpnp_hap *hap) /* 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; if (hap->pmic_subtype == PM660_SUBTYPE) { if (hap->lra_res_cal_period < QPNP_HAP_PM660_RES_CAL_PERIOD_MIN) 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; } reg = mask = 0; if (hap->pmic_subtype == PM660_SUBTYPE) { reg |= hap->auto_res_mode << QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT; mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT; reg |= hap->lra_high_z << QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT; mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK; if (hap->lra_qwd_drive_duration != -EINVAL) { reg |= hap->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) { 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); reg &= QPNP_HAP_LRA_RES_CAL_PER_MASK; mask |= QPNP_HAP_LRA_HIGH_Z_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)); 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) return rc; } else { Loading Loading @@ -1867,8 +1954,13 @@ static int qpnp_hap_config(struct qpnp_hap *hap) /* Configure the INTERNAL_PWM register */ if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) { if (hap->pmic_subtype == PM660_SUBTYPE) { 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) { hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; temp = 1; Loading Loading @@ -2108,20 +2200,36 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) } 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, "qcom,lra-auto-res-mode", &temp_str); if (!rc) { if (hap->pmic_subtype == PM660_SUBTYPE) { hap->auto_res_mode = QPNP_HAP_PM660_AUTO_RES_QWD; if (strcmp(temp_str, "zxd") == 0) hap->auto_res_mode = QPNP_HAP_PM660_AUTO_RES_ZXD; else if (strcmp(temp_str, "qwd") == 0) hap->auto_res_mode = QPNP_HAP_PM660_AUTO_RES_QWD; } else { 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; 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; 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; 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; hap->auto_res_mode = QPNP_HAP_AUTO_RES_MAX_QWD; else hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; } } else if (rc != -EINVAL) { dev_err(&pdev->dev, "Unable to read auto res mode\n"); return rc; Loading @@ -2133,6 +2241,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { if (strcmp(temp_str, "none") == 0) 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) hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1; else if (strcmp(temp_str, "opt2") == 0) Loading @@ -2144,6 +2257,15 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) 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; rc = of_property_read_u32(pdev->dev.of_node, "qcom,lra-res-cal-period", &temp); Loading Loading @@ -2321,6 +2443,34 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) 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) { struct qpnp_hap *hap; Loading Loading @@ -2350,6 +2500,12 @@ static int qpnp_haptic_probe(struct platform_device *pdev) 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); if (rc) { dev_err(&pdev->dev, "DT parsing failed\n"); Loading Loading
Documentation/devicetree/bindings/soc/qcom/qpnp-haptic.txt +9 −1 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ pwm(pulse width modulation) and audio. Required Properties: - compatible: must be "qcom,qpnp-haptic" - reg: address of device - qcom,pmic-revid : phandle to fetch PMIC revid - qcom,actuator-type: must be one of "erm" or "lra" - qcom,play-mode : must be one of "buffer", "direct", "pwm" or "audio" Loading Loading @@ -63,7 +64,13 @@ Optional properties when qcom,actuator-type is "lra" "max-qwd" : Maximum QWD "zxd-eop" : ZXD + End of pattern (This is the Default) - 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 4 to 32(default) - qcom,perform-lra-auto-resonance-search : boolean, define this property if: Loading Loading @@ -109,6 +116,7 @@ Example: interrupts = <0x3 0xc0 0x0>, <0x3 0xc0 0x1>; interrupt-names = "sc-irq", "play-irq"; qcom,pmic-revid = <&pm660_revid>; vcc_pon-supply = <&pon_perph_reg>; qcom,play-mode = "direct"; qcom,wave-play-rate-us = <5263>; Loading
arch/arm/boot/dts/qcom/msm-pm660.dtsi +3 −2 Original line number Diff line number Diff line Loading @@ -607,6 +607,7 @@ interrupts = <0x1 0xc0 0x0 IRQ_TYPE_NONE>, <0x1 0xc0 0x1 IRQ_TYPE_NONE>; interrupt-names = "sc-irq", "play-irq"; qcom,pmic-revid = <&pm660_revid>; qcom,actuator-type = "lra"; qcom,play-mode = "direct"; qcom,vmax-mv = <3200>; Loading @@ -619,9 +620,9 @@ qcom,brake-pattern = [03 03 00 00]; qcom,use-play-irq; qcom,use-sc-irq; qcom,lra-high-z = "opt1"; qcom,lra-high-z = "opt0"; qcom,lra-auto-res-mode = "qwd"; qcom,lra-res-cal-period = <4>; qcom,lra-calibrate-at-eop = <0>; qcom,correct-lra-drive-freq; qcom,misc-trim-error-rc19p2-clk-reg-present; }; Loading
arch/arm/boot/dts/qcom/msm-pmi8998.dtsi +1 −0 Original line number Diff line number Diff line Loading @@ -631,6 +631,7 @@ interrupts = <0x3 0xc0 0x0 IRQ_TYPE_NONE>, <0x3 0xc0 0x1 IRQ_TYPE_NONE>; interrupt-names = "sc-irq", "play-irq"; qcom,pmic-revid = <&pmi8998_revid>; qcom,actuator-type = "lra"; qcom,play-mode = "direct"; qcom,vmax-mv = <3200>; Loading
drivers/soc/qcom/qpnp-haptic.c +209 −53 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <linux/qpnp/pwm.h> #include <linux/err.h> #include <linux/delay.h> #include <linux/qpnp/qpnp-revid.h> #include <linux/qpnp/qpnp-haptic.h> #include "../../staging/android/timed_output.h" Loading @@ -37,6 +38,7 @@ #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_AUTO_RES_CTRL(b) (b + 0x4B) #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) Loading @@ -62,13 +64,25 @@ #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_MASK GENMASK(6, 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_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_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_SHFT 4 #define QPNP_HAP_VMAX_MASK 0xC1 Loading Loading @@ -130,7 +144,6 @@ #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 Loading Loading @@ -222,9 +235,14 @@ enum qpnp_hap_auto_res_mode { 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 */ 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_OPT2, QPNP_HAP_LRA_HIGH_Z_OPT3, Loading @@ -240,7 +258,7 @@ enum qpnp_hap_mode { /* status flags */ enum qpnp_hap_status { AUTO_RESONANCE_ENABLED = 1, AUTO_RESONANCE_ENABLED = BIT(0), }; /* pwm channel info */ Loading Loading @@ -271,6 +289,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 Loading Loading @@ -327,8 +346,9 @@ struct qpnp_hap { 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; int lra_qwd_drive_duration; int calibrate_at_eop; u32 init_drive_period_code; u32 timeout_ms; u32 time_required_to_generate_back_emf_us; Loading @@ -346,6 +366,7 @@ struct qpnp_hap { u16 base; 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; Loading @@ -355,9 +376,10 @@ struct qpnp_hap { 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; u8 pmic_subtype; u8 auto_res_mode; bool vcc_pon_enabled; bool state; bool use_play_irq; Loading Loading @@ -403,6 +425,21 @@ static int qpnp_hap_write_reg(struct qpnp_hap *hap, u8 *data, u16 addr) 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 */ static int qpnp_hap_sec_access(struct qpnp_hap *hap) { Loading Loading @@ -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) { 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; u8 val = 0; u16 addr; if (enable) val |= AUTO_RES_ENABLE; else val |= AUTO_RES_DISABLE; if (hap->pmic_subtype == PM660_SUBTYPE) { addr = QPNP_HAP_AUTO_RES_CTRL(hap->base); } else { addr = QPNP_HAP_TEST2_REG(hap->base); /* 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 (enable) val |= AUTO_RES_ENABLE; rc = qpnp_hap_masked_write_reg(hap, val, addr, AUTO_RES_ENABLE); if (rc) return rc; Loading Loading @@ -1512,6 +1549,7 @@ static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) /* set api for haptics */ static int qpnp_hap_set(struct qpnp_hap *hap, int 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; Loading @@ -1536,9 +1574,16 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) * 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 || hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)) auto_res_mode_qwd)) qpnp_hap_auto_res_enable(hap, 0); rc = qpnp_hap_mod_enable(hap, on); Loading @@ -1549,7 +1594,7 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) if ((hap->act_type == QPNP_HAP_LRA) && (hap->correct_lra_drive_freq || hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD)) { auto_res_mode_qwd)) { usleep_range(back_emf_delay_us, (back_emf_delay_us + 1)); Loading Loading @@ -1782,7 +1827,7 @@ 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, unlock_val; u8 reg = 0, unlock_val, mask; u32 temp; int rc, i; uint error_code = 0; Loading @@ -1804,24 +1849,66 @@ static int qpnp_hap_config(struct qpnp_hap *hap) /* 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; if (hap->pmic_subtype == PM660_SUBTYPE) { if (hap->lra_res_cal_period < QPNP_HAP_PM660_RES_CAL_PERIOD_MIN) 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; } reg = mask = 0; if (hap->pmic_subtype == PM660_SUBTYPE) { reg |= hap->auto_res_mode << QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT; mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT; reg |= hap->lra_high_z << QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT; mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK; if (hap->lra_qwd_drive_duration != -EINVAL) { reg |= hap->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) { 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); reg &= QPNP_HAP_LRA_RES_CAL_PER_MASK; mask |= QPNP_HAP_LRA_HIGH_Z_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)); 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) return rc; } else { Loading Loading @@ -1867,8 +1954,13 @@ static int qpnp_hap_config(struct qpnp_hap *hap) /* Configure the INTERNAL_PWM register */ if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) { if (hap->pmic_subtype == PM660_SUBTYPE) { 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) { hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; temp = 1; Loading Loading @@ -2108,20 +2200,36 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) } 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, "qcom,lra-auto-res-mode", &temp_str); if (!rc) { if (hap->pmic_subtype == PM660_SUBTYPE) { hap->auto_res_mode = QPNP_HAP_PM660_AUTO_RES_QWD; if (strcmp(temp_str, "zxd") == 0) hap->auto_res_mode = QPNP_HAP_PM660_AUTO_RES_ZXD; else if (strcmp(temp_str, "qwd") == 0) hap->auto_res_mode = QPNP_HAP_PM660_AUTO_RES_QWD; } else { 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; 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; 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; 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; hap->auto_res_mode = QPNP_HAP_AUTO_RES_MAX_QWD; else hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; } } else if (rc != -EINVAL) { dev_err(&pdev->dev, "Unable to read auto res mode\n"); return rc; Loading @@ -2133,6 +2241,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { if (strcmp(temp_str, "none") == 0) 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) hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1; else if (strcmp(temp_str, "opt2") == 0) Loading @@ -2144,6 +2257,15 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) 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; rc = of_property_read_u32(pdev->dev.of_node, "qcom,lra-res-cal-period", &temp); Loading Loading @@ -2321,6 +2443,34 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) 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) { struct qpnp_hap *hap; Loading Loading @@ -2350,6 +2500,12 @@ static int qpnp_haptic_probe(struct platform_device *pdev) 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); if (rc) { dev_err(&pdev->dev, "DT parsing failed\n"); Loading