From 680780f5e8376163a951d733e4c055b52875608e Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 15 Jul 2020 15:20:16 +0800 Subject: [PATCH 1/2] input: qcom-hv-haptics: notify HBOOST to keep VREG on during play HAPTICS module controls the output voltage of HAPTICS_BOOST module through hardware signals when playing a vibration. During FIFO mode playing, HAPTICS module would request HAPTICS_BOOST module to turn on/off the output based on FIFO empty status. HAPTICS_BOOST is turned off when FIFO is empty and it's turned on again when more FIFO samples are filled. During this voltage transition request (off->on), HAPTICS_BOOST module may have a race condition if this requests comes in rapid succession. This can lead to its FSM getting stuck. To avoid this race condition, notify HAPTICS_BOOST module to keep the output enabled until the playing is stopped. This is done by triggering a PBS sequence through a SDAM module. Change-Id: I5cef13df7a1fa4608dd64d509c802e0bcfde6b92 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 58 +++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 5239f81641b5..3cb88a278611 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -430,6 +430,7 @@ struct haptics_chip { struct regulator_dev *swr_slave_rdev; struct mutex irq_lock; struct nvmem_cell *cl_brake_nvmem; + struct nvmem_device *hap_cfg_nvmem; struct class hap_class; int fifo_empty_irq; u32 effects_count; @@ -892,6 +893,40 @@ static int haptics_set_direct_play(struct haptics_chip *chip, u8 amplitude) return rc; } +#define PBS_ARG_REG 0x42 +#define HAP_VREG_ON_VAL 0x1 +#define HAP_VREG_OFF_VAL 0x2 +#define PBS_TRIG_SET_REG 0xE5 +#define PBS_TRIG_SET_VAL 0x1 +static int haptics_boost_vreg_enable(struct haptics_chip *chip, bool en) +{ + int rc; + u8 val; + + if (chip->hap_cfg_nvmem == NULL) { + dev_dbg(chip->dev, "nvmem device for hap_cfg is not defined\n"); + return 0; + } + + val = en ? HAP_VREG_ON_VAL : HAP_VREG_OFF_VAL; + rc = nvmem_device_write(chip->hap_cfg_nvmem, + PBS_ARG_REG, 1, &val); + if (rc < 0) { + dev_err(chip->dev, "write SDAM %#x failed, rc=%d\n", + PBS_ARG_REG, rc); + return rc; + } + + val = PBS_TRIG_SET_VAL; + rc = nvmem_device_write(chip->hap_cfg_nvmem, + PBS_TRIG_SET_REG, 1, &val); + if (rc < 0) + dev_err(chip->dev, "Write SDAM %#x failed, rc=%d\n", + PBS_TRIG_SET_REG, rc); + + return rc; +} + static int haptics_enable_play(struct haptics_chip *chip, bool en) { struct haptics_play_info *play = &chip->play; @@ -916,8 +951,17 @@ static int haptics_enable_play(struct haptics_chip *chip, bool en) rc = haptics_write(chip, chip->cfg_addr_base, HAP_CFG_SPMI_PLAY_REG, &val, 1); - if (rc < 0) + if (rc < 0) { dev_err(chip->dev, "Write SPMI_PLAY failed, rc=%d\n", rc); + return rc; + } + + if (play->pattern_src == FIFO) { + rc = haptics_boost_vreg_enable(chip, en); + if (rc < 0) + dev_err(chip->dev, "Notify vreg %s failed, rc=%d\n", + en ? "enabling" : "disabling", rc); + } return rc; } @@ -3069,6 +3113,18 @@ static int haptics_parse_dt(struct haptics_chip *chip) } } + if (of_find_property(node, "nvmem", NULL)) { + chip->hap_cfg_nvmem = + devm_nvmem_device_get(chip->dev, "hap_cfg_sdam"); + if (IS_ERR(chip->hap_cfg_nvmem)) { + rc = PTR_ERR(chip->hap_cfg_nvmem); + if (rc != -EPROBE_DEFER) + dev_err(chip->dev, "Failed to get nvmem device, rc=%d\n", + rc); + return rc; + } + } + addr = of_get_address(node, 0, NULL, NULL); if (!addr) { dev_err(chip->dev, "Read HAPTICS_CFG address failed\n"); From 23b8d5475dfa013a7eebc212ba27e04880fe3e3e Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Tue, 7 Jul 2020 15:04:17 +0800 Subject: [PATCH 2/2] input: qcom-hv-haptics: update LRA period calculations There is a new scheme recommended in PM8350B 2.0 haptics module for getting close-loop LRA period. There are 4 situations need to be considered depending on if auto mode RC CLK calibration is used and if auto resonance calibration is done during the playing. In each situation, different status values and equations are used for calculating close-loop LRA period. Update the driver to support this. Change-Id: I8f5fdcdd3ebae71af333a0b05fb4860e1c43ce10 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 147 ++++++++++++++++++++++----- 1 file changed, 122 insertions(+), 25 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 3cb88a278611..fdb26327f295 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -29,7 +29,9 @@ #define AUTO_RES_CAL_DONE_BIT BIT(5) #define CAL_TLRA_CL_STS_MSB_MASK GENMASK(4, 0) /* STATUS_DATA_MSB definition in V2 while MOD_STATUS_SEL is 3 */ -#define LAST_GOOD_TLRA_CL_MASK GENMASK(4, 0) +#define LAST_GOOD_TLRA_CL_MSB_MASK GENMASK(4, 0) +/* STATUS_DATA_MSB definition in V2 while MOD_STATUS_SEL is 4 */ +#define TLRA_CL_ERR_MSB_MASK GENMASK(4, 0) /* STATUS_DATA_MSB definition in V1 while MOD_STATUS_SEL is 5 */ #define FIFO_REAL_TIME_FILL_STATUS_MASK_V1 GENMASK(6, 0) /* STATUS DATA_MSB definition in V2 while MOD_STATUS_SEL is 5 */ @@ -135,6 +137,7 @@ #define HAP_CFG_MOD_STATUS_SEL_REG 0x70 #define MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL 0 #define MOD_STATUS_SEL_LAST_GOOD_TLRA_VAL 3 +#define MOD_STATUS_SEL_TLRA_CL_ERR_STS_VAL 4 #define MOD_STATUS_SEL_FIFO_FILL_STATUS_VAL 5 #define MOD_STATUS_SEL_BRAKE_CAL_RNAT_RCAL_VAL 6 @@ -145,6 +148,7 @@ #define HAP_CFG_CAL_EN_REG 0x72 #define CAL_RC_CLK_MASK GENMASK(3, 2) #define CAL_RC_CLK_SHIFT 2 +#define CAL_RC_CLK_DISABLED_VAL 0 #define CAL_RC_CLK_AUTO_VAL 1 #define CAL_RC_CLK_MANUAL_VAL 2 @@ -757,25 +761,48 @@ static int haptics_get_closeloop_lra_period_v1( tmp = ((val[0] & CAL_TLRA_CL_STS_MSB_MASK) << 8) | val[1]; config->cl_t_lra_us = (tmp * step_ns) / 1000; - return 0; + return haptics_adjust_lra_period(chip, &config->cl_t_lra_us); } -#define CL_TLRA_STEP_NS 1666 +#define TLRA_AUTO_RES_ERR_NO_CAL_STEP_PSEC 1667000 +#define TLRA_AUTO_RES_NO_CAL_STEP_PSEC 3333000 +#define TLRA_AUTO_RES_ERR_AUTO_CAL_STEP_PSEC 1627700 +#define TLRA_AUTO_RES_AUTO_CAL_STEP_PSEC 813850 static int haptics_get_closeloop_lra_period_v2( struct haptics_chip *chip) { struct haptics_hw_config *config = &chip->config; + u16 cal_tlra_cl_sts, tlra_cl_err_sts, tlra_ol, last_good_tlra_cl_sts; + u8 val[2], rc_clk_cal; + bool auto_res_done; + u64 tmp; int rc; - u8 val[2]; - u32 tmp; - val[0] = MOD_STATUS_XT_SEL_LAST_GOOD_TLRA_VAL; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_MOD_STATUS_XT_V2_REG, val, 1); + /* read RC_CLK_CAL enabling mode */ + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, val, 1); if (rc < 0) return rc; - val[0] = MOD_STATUS_SEL_LAST_GOOD_TLRA_VAL; + rc_clk_cal = ((val[0] & CAL_RC_CLK_MASK) >> CAL_RC_CLK_SHIFT); + /* read auto resonance calibration result */ + val[0] = MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, val, 1); + if (rc < 0) + return rc; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_STATUS_DATA_MSB_REG, val, 2); + if (rc < 0) + return rc; + + auto_res_done = !!(val[0] & AUTO_RES_CAL_DONE_BIT); + cal_tlra_cl_sts = + ((val[0] & CAL_TLRA_CL_STS_MSB_MASK) << 8) | val[1]; + + /* read auto resonance calibration error status */ + val[0] = MOD_STATUS_SEL_TLRA_CL_ERR_STS_VAL; rc = haptics_write(chip, chip->cfg_addr_base, HAP_CFG_MOD_STATUS_SEL_REG, val, 1); if (rc < 0) @@ -785,15 +812,77 @@ static int haptics_get_closeloop_lra_period_v2( HAP_CFG_STATUS_DATA_MSB_REG, val, 2); if (rc < 0) return rc; - /* - * Calculate the closed loop T_LRA with the following equations - * for PM8350B V2: - * LAST_GOOD_TLRA_CL_STS[12:0] = STATUS_DATA_MSB[4:0] | - * STATUS_DATA_LSB[7:0] - * LAST_GOOD_TLRA_CL = LAST_GOOD_TLRA_CL_STS[12:0] * 1.666 us - */ - tmp = ((val[0] & LAST_GOOD_TLRA_CL_MASK) << 8) | val[1]; - config->cl_t_lra_us = (tmp * CL_TLRA_STEP_NS) / 1000; + + tlra_cl_err_sts = + ((val[0] & TLRA_CL_ERR_MSB_MASK) << 8) | val[1]; + + dev_dbg(chip->dev, "rc_clk_cal = %u, auto_res_done = %d\n", + rc_clk_cal, auto_res_done); + + if (rc_clk_cal == CAL_RC_CLK_DISABLED_VAL && !auto_res_done) { + /* TLRA_CL_ERR(us) = TLRA_CL_ERR_STS * 1.667 us */ + tmp = tlra_cl_err_sts * TLRA_AUTO_RES_ERR_NO_CAL_STEP_PSEC; + } else if (rc_clk_cal == CAL_RC_CLK_DISABLED_VAL && auto_res_done) { + /* + * CAL_TLRA_CL_STS_NO_CAL = CAL_TLRA_CL_STS + * TLRA_AUTO_RES(us) = CAL_TLRA_CL_STS_NO_CAL * 3.333 us + */ + tmp = cal_tlra_cl_sts * TLRA_AUTO_RES_NO_CAL_STEP_PSEC; + } else if (rc_clk_cal == CAL_RC_CLK_AUTO_VAL && !auto_res_done) { + /* + * CAL_TLRA_OL = CAL_TLRA_CL_STS; + * TLRA_CL_ERR(us) = TLRA_CL_ERR_STS * + * (TLRA_OL / CAL_TLRA_OL) * 1.6277 us + */ + + /* read the TLRA_OL setting */ + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_TLRA_OL_HIGH_REG, val, 2); + if (rc < 0) + return rc; + + tlra_ol = (val[0] & TLRA_OL_MSB_MASK) << 8 | val[1]; + tmp = tlra_cl_err_sts * tlra_ol; + tmp *= TLRA_AUTO_RES_ERR_AUTO_CAL_STEP_PSEC; + tmp = div_u64(tmp, cal_tlra_cl_sts); + } else if (rc_clk_cal == CAL_RC_CLK_AUTO_VAL && auto_res_done) { + /* + * CAL_TLRA_CL_STS_W_CAL = CAL_TLRA_CL_STS; + * TLRA_AUTO_RES(us) = LAST_GOOD_TLRA_CL_STS * 0.81385 us * + * (LAST_GOOD_TLRA_CL_STS / CAL_TLRA_CL_STS_AUTO_CAL) + */ + + /* read LAST_GOOD_TLRA_CL_STS */ + val[0] = MOD_STATUS_XT_SEL_LAST_GOOD_TLRA_VAL; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_XT_V2_REG, val, 1); + if (rc < 0) + return rc; + + val[0] = MOD_STATUS_SEL_LAST_GOOD_TLRA_VAL; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_MOD_STATUS_SEL_REG, val, 1); + if (rc < 0) + return rc; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_STATUS_DATA_MSB_REG, val, 2); + if (rc < 0) + return rc; + + last_good_tlra_cl_sts = + ((val[0] & LAST_GOOD_TLRA_CL_MSB_MASK) << 8) | val[1]; + + tmp = last_good_tlra_cl_sts * last_good_tlra_cl_sts; + tmp *= TLRA_AUTO_RES_AUTO_CAL_STEP_PSEC; + tmp = div_u64(tmp, cal_tlra_cl_sts); + } else { + dev_err(chip->dev, "Can't get close-loop LRA period in rc_clk_cal mode %u\n", + rc_clk_cal); + return -EINVAL; + } + + config->cl_t_lra_us = div_u64(tmp, 1000000); return 0; } @@ -808,17 +897,13 @@ static int haptics_get_closeloop_lra_period(struct haptics_chip *chip) rc = haptics_get_closeloop_lra_period_v2(chip); if (rc < 0) { - dev_err(chip->dev, "get close loop T LRA failed, rc=%d\n", rc); + dev_err(chip->dev, "get close loop T LRA failed, rc=%d\n", + rc); return rc; } - rc = haptics_adjust_lra_period(chip, &chip->config.cl_t_lra_us); - if (rc < 0) - return rc; - dev_dbg(chip->dev, "OL_TLRA %u us, CL_TLRA %u us\n", chip->config.t_lra_us, chip->config.cl_t_lra_us); - return 0; } @@ -1918,6 +2003,7 @@ static int haptics_hw_init(struct haptics_chip *chip) struct haptics_effect *effect; int rc = 0, i; u8 val[2]; + u32 t_lra_us; /* Store CL brake settings */ rc = haptics_store_cl_brake_settings(chip); @@ -1962,12 +2048,23 @@ static int haptics_hw_init(struct haptics_chip *chip) if (config->is_erm) return 0; + /* set AUTO_mode RC CLK calibration by default */ + val[0] = CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT; + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, CAL_RC_CLK_MASK, val[0]); + if (rc < 0) + return rc; + rc = haptics_get_closeloop_lra_period(chip); if (rc < 0) return rc; /* Config T_LRA */ - rc = haptics_config_openloop_lra_period(chip, chip->config.cl_t_lra_us); + t_lra_us = chip->config.t_lra_us; + if (chip->config.cl_t_lra_us != 0) + t_lra_us = chip->config.cl_t_lra_us; + + rc = haptics_config_openloop_lra_period(chip, t_lra_us); if (rc < 0) return rc;