From 9837851fd1cacf115393b53e2cdf5125ca68cf1b Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 1 May 2020 19:31:10 +0800 Subject: [PATCH 1/4] input: qcom-hv-haptics: Add LRA frequency calibration interfaces Add interfaces to run calibration sequence and read out the closed loop LRA frequency. With this change, the closed loop LRA frequency can be read out using following commands: echo 1 > /sys/class/qcom-haptics/lra_calibration cat /sys/class/qcom-haptics/lra_frequency_hz Meanwhile, correct the setting for direct play, it expects a 8-bit value to represent the proportion of vmax instead of the actual vmax voltage. Change-Id: I9f3bfb978240ca853446dee4877d6ece4b465058 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 312 +++++++++++++++++++++++---- 1 file changed, 267 insertions(+), 45 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 5a5932ef6c65..0f97df876113 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -29,7 +29,7 @@ #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_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 */ @@ -104,6 +104,14 @@ #define HAP_CFG_AUTORES_CFG_REG 0x63 #define AUTORES_EN_BIT BIT(7) +#define AUTORES_EN_DLY_MASK GENMASK(5, 2) +#define AUTORES_EN_DLY_1_CYCLE 0x2 +#define AUTORES_EN_DLY_SHIFT 2 +#define AUTORES_ERR_WINDOW_MASK GENMASK(1, 0) +#define AUTORES_ERR_WINDOW_12P5_PERCENT 0x0 +#define AUTORES_ERR_WINDOW_25_PERCENT 0x1 +#define AUTORES_ERR_WINDOW_50_PERCENT 0x2 +#define AUTORES_ERR_WINDOW_100_PERCENT 0x3 #define HAP_CFG_AUTORES_ERR_RECOVERY_REG 0x64 #define EN_HW_RECOVERY_BIT BIT(1) @@ -117,6 +125,7 @@ #define HAP_CFG_VMAX_HDRM_REG 0x67 #define VMAX_HDRM_MASK GENMASK(6, 0) #define VMAX_HDRM_STEP_MV 50 +#define VMAX_HDRM_MAX_MV 6350 #define HAP_CFG_MOD_STATUS_SEL_REG 0x70 #define MOD_STATUS_SEL_CAL_TLRA_CL_STS_VAL 0 @@ -128,8 +137,8 @@ #define MOD_STATUS_XT_V2_FIFO_FILL_STATUS_VAL 0x80 #define MOD_STATUS_XT_SEL_LAST_GOOD_TLRA_VAL 0 -#define HAP_CFG_CAL_EN_REG 0x72 -#define CAL_RC_CLK_MASK GENMASK(3, 2) +#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_MANUAL_VAL 2 @@ -172,6 +181,7 @@ /* shared registers between V1 and V2 chips */ #define HAP_PTN_DIRECT_PLAY_REG 0x26 +#define DIRECT_PLAY_MAX_AMPLITUDE 0xFF #define HAP_PTN_AUTORES_CAL_CFG_REG 0x28 @@ -364,9 +374,11 @@ struct haptics_play_info { struct haptics_effect *effect; struct brake_cfg *brake; struct fifo_play_status fifo_status; + struct mutex lock; u32 vmax_mv; u32 length_us; enum pattern_src pattern_src; + bool in_calibration; }; struct haptics_hw_config { @@ -399,6 +411,7 @@ struct haptics_chip { struct regulator_dev *swr_slave_rdev; struct mutex irq_lock; struct nvmem_cell *cl_brake_nvmem; + struct class hap_class; int fifo_empty_irq; u32 effects_count; u32 cfg_addr_base; @@ -661,8 +674,8 @@ static int get_brake_play_length_us(struct brake_cfg *brake, u32 t_lra_us) return t_lra_us * (i + 1); } -#define V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_NOT_DONE 5000 -#define V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_DONE 3333 +#define V1_CL_TLRA_STEP_AUTO_RES_CAL_NOT_DONE_NS 5000 +#define V1_CL_TLRA_STEP_AUTO_RES_CAL_DONE_NS 3333 static int haptics_get_closeloop_lra_period_v1( struct haptics_chip *chip) { @@ -694,9 +707,9 @@ static int haptics_get_closeloop_lra_period_v1( */ auto_res_cal_done = !!(val[0] & AUTO_RES_CAL_DONE_BIT); if (auto_res_cal_done) - step_ns = V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_DONE; + step_ns = V1_CL_TLRA_STEP_AUTO_RES_CAL_DONE_NS; else - step_ns = V1_CL_TLRA_STEP_NS_AUTO_RES_CAL_NOT_DONE; + step_ns = V1_CL_TLRA_STEP_AUTO_RES_CAL_NOT_DONE_NS; tmp = ((val[0] & CAL_TLRA_CL_STS_MSB_MASK) << 8) | val[1]; config->cl_t_lra_us = (tmp * step_ns) / 1000; @@ -778,6 +791,26 @@ static int haptics_set_vmax_mv(struct haptics_chip *chip, u32 vmax_mv) return rc; } +static int haptics_set_vmax_headroom_mv(struct haptics_chip *chip, u32 hdrm_mv) +{ + int rc = 0; + u8 val; + + if (hdrm_mv > VMAX_HDRM_MAX_MV) { + dev_err(chip->dev, "headroom (%d) exceed the max value: %d\n", + hdrm_mv, VMAX_HDRM_MAX_MV); + return -EINVAL; + } + + val = hdrm_mv / VMAX_HDRM_STEP_MV; + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_VMAX_HDRM_REG, &val, 1); + if (rc < 0) + dev_err(chip->dev, "config VMAX_HDRM failed, rc=%d\n", rc); + + return rc; +} + static int haptics_enable_autores(struct haptics_chip *chip, bool en) { int rc; @@ -792,6 +825,18 @@ static int haptics_enable_autores(struct haptics_chip *chip, bool en) return rc; } +static int haptics_set_direct_play(struct haptics_chip *chip, u8 amplitude) +{ + int rc; + + rc = haptics_write(chip, chip->ptn_addr_base, + HAP_PTN_DIRECT_PLAY_REG, &litude, 1); + if (rc < 0) + dev_err(chip->dev, "config DIRECT_PLAY failed, rc=%d\n", rc); + + return rc; +} + static int haptics_enable_play(struct haptics_chip *chip, bool en) { struct haptics_play_info *play = &chip->play; @@ -1010,8 +1055,7 @@ static int haptics_get_available_fifo_memory(struct haptics_chip *chip) static int haptics_adjust_rc_clk(struct haptics_chip *chip) { - int rc; - int freq_diff, cal_count, f_lra, cl_f_lra; + int rc, freq_diff, cal_count, f_lra, cl_f_lra; u8 val[2]; if (!chip->config.t_lra_us || !chip->config.cl_t_lra_us) @@ -1182,51 +1226,56 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo) return haptics_set_fifo_empty_threshold(chip, fifo_thresh); } -static int haptics_set_direct_play(struct haptics_chip *chip) +static int haptics_load_constant_effect(struct haptics_chip *chip, u8 amplitude) { struct haptics_play_info *play = &chip->play; int rc = 0; - u8 val; + + mutex_lock(&chip->play.lock); + if (chip->play.in_calibration) { + dev_err(chip->dev, "calibration in progress, ignore playing constant effect\n"); + rc = -EBUSY; + goto unlock; + } /* configure VMAX in case it was changed in previous effect playing */ rc = haptics_set_vmax_mv(chip, chip->config.vmax_mv); if (rc < 0) - return rc; - - /* Set DIRECT_PLAY amplitude */ - val = play->vmax_mv / VMAX_STEP_MV; - rc = haptics_write(chip, chip->ptn_addr_base, - HAP_PTN_DIRECT_PLAY_REG, &val, 1); - if (rc < 0) - return rc; + goto unlock; /* Config brake settings if it's necessary */ play->brake = &chip->config.brake; if (play->brake) { rc = haptics_set_brake(chip, play->brake); if (rc < 0) - return rc; + goto unlock; } + rc = haptics_set_direct_play(chip, amplitude); + if (rc < 0) + goto unlock; + /* Always enable LRA auto resonance for DIRECT_PLAY */ rc = haptics_enable_autores(chip, !chip->config.is_erm); if (rc < 0) - return rc; + goto unlock; play->pattern_src = DIRECT_PLAY; - return 0; +unlock: + mutex_unlock(&chip->play.lock); + return rc; } static int haptics_load_predefined_effect(struct haptics_chip *chip, - int effect_idx) + struct haptics_effect *effect) { struct haptics_play_info *play = &chip->play; int rc; - if (effect_idx >= chip->effects_count) + if (effect == NULL) return -EINVAL; - play->effect = &chip->effects[effect_idx]; + play->effect = effect; /* Clamp VMAX for different vibration strength */ rc = haptics_set_vmax_mv(chip, play->vmax_mv); if (rc < 0) @@ -1393,12 +1442,19 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, fifo->play_length_us = get_fifo_play_length_us(fifo, chip->custom_effect->t_lra_us); + mutex_lock(&chip->play.lock); + if (chip->play.in_calibration) { + dev_err(chip->dev, "calibration in progress, ignore playing custom effect\n"); + rc = -EBUSY; + goto unlock; + } + play->effect = chip->custom_effect; play->brake = NULL; play->vmax_mv = (magnitude * chip->custom_effect->vmax_mv) / 0x7fff; rc = haptics_set_vmax_mv(chip, play->vmax_mv); if (rc < 0) - goto cleanup; + goto unlock; rc = haptics_enable_autores(chip, !play->effect->auto_res_disable); if (rc < 0) @@ -1407,9 +1463,12 @@ static int haptics_load_custom_effect(struct haptics_chip *chip, play->pattern_src = FIFO; rc = haptics_set_fifo(chip, play->effect->fifo); if (rc < 0) - goto cleanup; + goto unlock; + mutex_unlock(&chip->play.lock); return 0; +unlock: + mutex_unlock(&chip->play.lock); cleanup: kfree(fifo->samples); fifo->samples = NULL; @@ -1456,16 +1515,25 @@ static int haptics_load_periodic_effect(struct haptics_chip *chip, return -EINVAL; } + mutex_lock(&chip->play.lock); play->vmax_mv = (magnitude * chip->effects[i].vmax_mv) / 0x7fff; dev_dbg(chip->dev, "upload effect %d, vmax_mv=%d\n", chip->effects[i].id, play->vmax_mv); - rc = haptics_load_predefined_effect(chip, i); + + if (chip->play.in_calibration) { + dev_err(chip->dev, "calibration in progress, ignore playing predefined effect\n"); + rc = -EBUSY; + goto unlock; + } + + rc = haptics_load_predefined_effect(chip, &chip->effects[i]); if (rc < 0) { dev_err(chip->dev, "Play predefined effect%d failed, rc=%d\n", chip->effects[i].id, rc); - return rc; + goto unlock; } + mutex_unlock(&chip->play.lock); play->length_us = get_play_length_us(play); custom_data[CUSTOM_DATA_TIMEOUT_SEC_IDX] = @@ -1477,25 +1545,29 @@ static int haptics_load_periodic_effect(struct haptics_chip *chip, return -EFAULT; return 0; +unlock: + mutex_unlock(&chip->play.lock); + return rc; } static int haptics_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old) { struct haptics_chip *chip = input_get_drvdata(dev); - struct haptics_hw_config *config = &chip->config; - struct haptics_play_info *play = &chip->play; + u32 length_us, tmp; s16 level; + u8 amplitude; int rc = 0; switch (effect->type) { case FF_CONSTANT: - play->length_us = effect->replay.length * USEC_PER_MSEC; + length_us = effect->replay.length * USEC_PER_MSEC; level = effect->u.constant.level; - play->vmax_mv = (level * config->vmax_mv) / 0x7fff; - dev_dbg(chip->dev, "upload constant effect, length = %dus, vmax_mv = %d\n", - play->length_us, play->vmax_mv); - haptics_set_direct_play(chip); + tmp = level * DIRECT_PLAY_MAX_AMPLITUDE; + amplitude = tmp / 0x7fff; + dev_dbg(chip->dev, "upload constant effect, length = %dus, amplitude = %d\n", + length_us, amplitude); + haptics_load_constant_effect(chip, amplitude); if (rc < 0) { dev_err(chip->dev, "set direct play failed, rc=%d\n", rc); @@ -1657,12 +1729,26 @@ static int haptics_store_cl_brake_settings(struct haptics_chip *chip) return rc; } +static int haptics_config_openloop_lra_period(struct haptics_chip *chip, + u32 t_lra_us) +{ + u32 tmp; + u8 val[2]; + + tmp = t_lra_us / TLRA_STEP_US; + val[0] = (tmp >> 8) & TLRA_OL_MSB_MASK; + val[1] = tmp & TLRA_OL_LSB_MASK; + + return haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_TLRA_OL_HIGH_REG, val, 2); +} + static int haptics_hw_init(struct haptics_chip *chip) { struct haptics_hw_config *config = &chip->config; struct haptics_effect *effect; - int rc = 0, tmp, i; - u8 val[2]; + int rc = 0, i; + u8 val; /* Store CL brake settings */ rc = haptics_store_cl_brake_settings(chip); @@ -1682,13 +1768,13 @@ static int haptics_hw_init(struct haptics_chip *chip) return rc; /* Config brake mode and waveform shape */ - val[0] = (config->brake.mode << BRAKE_MODE_SHIFT) + val = (config->brake.mode << BRAKE_MODE_SHIFT) | config->brake.sine_gain << BRAKE_SINE_GAIN_SHIFT | config->brake.brake_wf; rc = haptics_masked_write(chip, chip->cfg_addr_base, HAP_CFG_BRAKE_MODE_CFG_REG, BRAKE_MODE_MASK | BRAKE_SINE_GAIN_MASK - | BRAKE_WF_SEL_MASK, val[0]); + | BRAKE_WF_SEL_MASK, val); if (rc < 0) return rc; @@ -1700,11 +1786,7 @@ static int haptics_hw_init(struct haptics_chip *chip) return rc; /* Config T_LRA */ - tmp = config->t_lra_us / TLRA_STEP_US; - val[0] = (tmp >> 8) & TLRA_OL_MSB_MASK; - val[1] = tmp & TLRA_OL_LSB_MASK; - rc = haptics_write(chip, chip->cfg_addr_base, - HAP_CFG_TLRA_OL_HIGH_REG, val, 2); + rc = haptics_config_openloop_lra_period(chip, chip->config.cl_t_lra_us); if (rc < 0) return rc; @@ -2971,6 +3053,136 @@ static int haptics_init_swr_slave_regulator(struct haptics_chip *chip) return rc; } +#define LRA_CALIBRATION_VMAX_HDRM_MV 500 +static int haptics_start_lra_calibration(struct haptics_chip *chip) +{ + int rc; + u8 autores_cfg; + + mutex_lock(&chip->play.lock); + /* + * Ignore calibration if it's in FIFO playing to avoid + * messing up the FIFO playing status + */ + if ((chip->play.pattern_src == FIFO) && + atomic_read(&chip->play.fifo_status.is_busy)) { + dev_err(chip->dev, "In FIFO playing, ignore calibration\n"); + rc = -EBUSY; + goto unlock; + } + + /* Stop other mode playing if there is any */ + rc = haptics_enable_play(chip, false); + if (rc < 0) { + dev_err(chip->dev, "Stop playing failed, rc=%d\n", rc); + goto unlock; + } + + chip->play.in_calibration = true; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_AUTORES_CFG_REG, &autores_cfg, 1); + if (rc < 0) { + dev_err(chip->dev, "Read AUTORES_CFG failed, rc=%d\n", rc); + goto unlock; + } + + rc = haptics_masked_write(chip, chip->cfg_addr_base, + HAP_CFG_AUTORES_CFG_REG, AUTORES_EN_BIT | + AUTORES_EN_DLY_MASK | AUTORES_ERR_WINDOW_MASK, + AUTORES_EN_DLY_1_CYCLE << AUTORES_EN_DLY_SHIFT + | AUTORES_ERR_WINDOW_25_PERCENT | AUTORES_EN_BIT); + if (rc < 0) + goto unlock; + + rc = haptics_config_openloop_lra_period(chip, chip->config.t_lra_us); + if (rc < 0) + goto restore; + + rc = haptics_set_vmax_mv(chip, chip->config.vmax_mv); + if (rc < 0) + goto restore; + + rc = haptics_set_vmax_headroom_mv(chip, LRA_CALIBRATION_VMAX_HDRM_MV); + if (rc < 0) + goto restore; + + rc = haptics_set_direct_play(chip, DIRECT_PLAY_MAX_AMPLITUDE); + if (rc < 0) + goto restore; + + chip->play.pattern_src = DIRECT_PLAY; + rc = haptics_enable_play(chip, true); + if (rc < 0) { + dev_err(chip->dev, "Start calibration failed, rc=%d\n", rc); + goto restore; + } + + /* wait for ~60ms to get the LRA calibration result */ + usleep_range(60000, 65000); + + rc = haptics_get_closeloop_lra_period(chip); + if (rc < 0) + goto restore; + + rc = haptics_enable_play(chip, false); + if (rc < 0) + goto restore; + + haptics_config_openloop_lra_period(chip, chip->config.cl_t_lra_us); + +restore: + rc = haptics_write(chip, chip->cfg_addr_base, + HAP_CFG_AUTORES_CFG_REG, &autores_cfg, 1); +unlock: + chip->play.in_calibration = false; + mutex_unlock(&chip->play.lock); + return rc; +} + +static ssize_t lra_calibration_store(struct class *c, + struct class_attribute *attr, const char *buf, size_t count) +{ + struct haptics_chip *chip = container_of(c, + struct haptics_chip, hap_class); + bool val; + int rc; + + if (kstrtobool(buf, &val)) + return -EINVAL; + + if (val) { + rc = haptics_start_lra_calibration(chip); + if (rc < 0) + return rc; + } + + return count; +} +static CLASS_ATTR_WO(lra_calibration); + +static ssize_t lra_frequency_hz_show(struct class *c, + struct class_attribute *attr, char *buf) +{ + struct haptics_chip *chip = container_of(c, + struct haptics_chip, hap_class); + u32 cl_f_lra; + + if (chip->config.cl_t_lra_us == 0) + return -EINVAL; + + cl_f_lra = USEC_PER_SEC / chip->config.cl_t_lra_us; + return scnprintf(buf, PAGE_SIZE, "%d Hz\n", cl_f_lra); +} +static CLASS_ATTR_RO(lra_frequency_hz); + +static struct attribute *hap_class_attrs[] = { + &class_attr_lra_calibration.attr, + &class_attr_lra_frequency_hz.attr, + NULL, +}; +ATTRIBUTE_GROUPS(hap_class); + static int haptics_probe(struct platform_device *pdev) { struct haptics_chip *chip; @@ -3028,6 +3240,7 @@ static int haptics_probe(struct platform_device *pdev) } mutex_init(&chip->irq_lock); + mutex_init(&chip->play.lock); disable_irq_nosync(chip->fifo_empty_irq); chip->fifo_empty_irq_en = false; @@ -3070,6 +3283,14 @@ static int haptics_probe(struct platform_device *pdev) } dev_set_drvdata(chip->dev, chip); + chip->hap_class.name = "qcom-haptics"; + chip->hap_class.class_groups = hap_class_groups; + rc = class_register(&chip->hap_class); + if (rc < 0) { + dev_err(chip->dev, "register hap_class failed, rc=%d\n"); + goto destroy_ff; + } + #ifdef CONFIG_DEBUG_FS rc = haptics_create_debugfs(chip); if (rc < 0) @@ -3085,6 +3306,7 @@ static int haptics_remove(struct platform_device *pdev) { struct haptics_chip *chip = dev_get_drvdata(&pdev->dev); + class_unregister(&chip->hap_class); #ifdef CONFIG_DEBUG_FS debugfs_remove_recursive(chip->debugfs_dir); #endif From 095f8ed8600e546f9cd878111fd52f17ce40edf8 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 13 May 2020 17:10:48 +0800 Subject: [PATCH 2/4] input: qcom-hv-haptics: Add debugfs files for more brake settings Add debugfs parameters per effect to enable brake and brake sine waveform gain setting. Meanwhile, change to print the FIFO samples with signed magnitude values so that they can be used offline to plot pattern waveform. Change-Id: Iaeeeedbba570333c8c4fa9793e2b4e9c1b4af09a Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 72 ++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 0f97df876113..3a1544c6ff4b 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -502,8 +502,8 @@ static void __dump_effects(struct haptics_chip *chip) pos = 0; pos += scnprintf(str, size, "%s", "FIFO data: "); for (j = 0; j < effect->fifo->num_s; j++) - pos += scnprintf(str + pos, size - pos, "%#x ", - effect->fifo->samples[j]); + pos += scnprintf(str + pos, size - pos, "%d ", + (s8)effect->fifo->samples[j]); dev_dbg(chip->dev, "%s\n", str); kfree(str); @@ -2058,7 +2058,7 @@ static int pattern_play_rate_us_dbgfs_write(void *data, u64 val) } DEFINE_DEBUGFS_ATTRIBUTE(pattern_play_rate_dbgfs_ops, pattern_play_rate_us_dbgfs_read, - pattern_play_rate_us_dbgfs_write, "%lld\n"); + pattern_play_rate_us_dbgfs_write, "%llu\n"); static ssize_t fifo_s_dbgfs_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos) @@ -2076,7 +2076,7 @@ static ssize_t fifo_s_dbgfs_read(struct file *fp, for (i = 0; i < fifo->num_s; i++) pos += scnprintf(kbuf + pos, size - pos, - "0x%03x ", fifo->samples[i]); + "%d ", (s8)fifo->samples[i]); pos += scnprintf(kbuf + pos, size - pos, "%s", "\n"); rc = simple_read_from_buffer(buf, count, ppos, kbuf, pos); @@ -2092,7 +2092,7 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp, struct fifo_cfg *fifo = effect->fifo; char *kbuf, *token; int rc, i = 0; - u32 val; + int val; u8 *samples; kbuf = kzalloc(count + 1, GFP_KERNEL); @@ -2115,7 +2115,7 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp, } while ((token = strsep(&kbuf, " ")) != NULL) { - rc = kstrtouint(token, 0, &val); + rc = kstrtoint(token, 0, &val); if (rc < 0) { rc = -EINVAL; goto exit2; @@ -2124,7 +2124,7 @@ static ssize_t fifo_s_dbgfs_write(struct file *fp, if (val > 0xff) val = 0xff; - samples[i++] = val; + samples[i++] = (u8)val; /* only support fifo pattern no longer than before */ if (i >= fifo->num_s) break; @@ -2182,7 +2182,7 @@ static int fifo_period_dbgfs_write(void *data, u64 val) DEFINE_DEBUGFS_ATTRIBUTE(fifo_period_dbgfs_ops, fifo_period_dbgfs_read, - fifo_period_dbgfs_write, "%lld\n"); + fifo_period_dbgfs_write, "%llu\n"); static ssize_t brake_s_dbgfs_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos) @@ -2327,6 +2327,50 @@ static const struct file_operations brake_mode_dbgfs_ops = { .open = simple_open, }; +static int brake_en_dbgfs_read(void *data, u64 *val) +{ + struct haptics_effect *effect = data; + + *val = !effect->brake->disabled; + + return 0; +} + +static int brake_en_dbgfs_write(void *data, u64 val) +{ + struct haptics_effect *effect = data; + + effect->brake->disabled = !val; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(brake_en_dbgfs_ops, brake_en_dbgfs_read, + brake_en_dbgfs_write, "%llu\n"); + +static int brake_sine_gain_dbgfs_read(void *data, u64 *val) +{ + struct haptics_effect *effect = data; + + *val = effect->brake->sine_gain; + + return 0; +} + +static int brake_sine_gain_dbgfs_write(void *data, u64 val) +{ + struct haptics_effect *effect = data; + + if (val > BRAKE_SINE_GAIN_X8) + return -EINVAL; + + effect->brake->sine_gain = val; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(brake_sine_gain_dbgfs_ops, + brake_sine_gain_dbgfs_read, + brake_sine_gain_dbgfs_write, "%llu\n"); + static int preload_effect_idx_dbgfs_read(void *data, u64 *val) { struct haptics_chip *chip = data; @@ -2373,7 +2417,7 @@ static int preload_effect_idx_dbgfs_write(void *data, u64 val) DEFINE_DEBUGFS_ATTRIBUTE(preload_effect_idx_dbgfs_ops, preload_effect_idx_dbgfs_read, - preload_effect_idx_dbgfs_write, "%lld\n"); + preload_effect_idx_dbgfs_write, "%llu\n"); static int haptics_add_effects_debugfs(struct haptics_effect *effect, struct dentry *dir) @@ -2436,6 +2480,16 @@ static int haptics_add_effects_debugfs(struct haptics_effect *effect, effect, &brake_mode_dbgfs_ops); if (IS_ERR(file)) return PTR_ERR(file); + + file = debugfs_create_file_unsafe("enable", 0644, brake_dir, + effect, &brake_en_dbgfs_ops); + if (IS_ERR(file)) + return PTR_ERR(file); + + file = debugfs_create_file_unsafe("sine_gain", 0644, brake_dir, + effect, &brake_sine_gain_dbgfs_ops); + if (IS_ERR(file)) + return PTR_ERR(file); } return 0; From 7ca15a569ec496d9845f2c6f86cdbb9f24665670 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Fri, 15 May 2020 13:59:36 +0800 Subject: [PATCH 3/4] input: qcom-hv-haptics: check FIFO fill status before stopping play Check the FIFO real time fill status and only stop FIFO playing in FIFO empty interrupt handler after confirming all FIFO samples have been played. Change-Id: I634f424c960893e22456734f30ae28f5030ca9e1 Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 85 +++++++++++++++++----------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 3a1544c6ff4b..69caba0cad32 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -1629,6 +1629,33 @@ static void haptics_fifo_empty_irq_config(struct haptics_chip *chip, mutex_unlock(&chip->irq_lock); } +static int haptics_stop_fifo_play(struct haptics_chip *chip) +{ + int rc; + + if (atomic_read(&chip->play.fifo_status.is_busy) == 0) { + dev_dbg(chip->dev, "FIFO playing is not in progress\n"); + return 0; + } + + rc = haptics_enable_play(chip, false); + if (rc < 0) + return rc; + + /* restore FIFO play rate back to T_LRA */ + rc = haptics_set_fifo_playrate(chip, T_LRA); + if (rc < 0) + return rc; + + haptics_fifo_empty_irq_config(chip, false); + kfree(chip->custom_effect->fifo->samples); + chip->custom_effect->fifo->samples = NULL; + + atomic_set(&chip->play.fifo_status.is_busy, 0); + dev_dbg(chip->dev, "stopped FIFO playing successfully\n"); + return 0; +} + static int haptics_playback(struct input_dev *dev, int effect_id, int val) { struct haptics_chip *chip = input_get_drvdata(dev); @@ -1644,7 +1671,8 @@ static int haptics_playback(struct input_dev *dev, int effect_id, int val) if (play->pattern_src == FIFO) haptics_fifo_empty_irq_config(chip, true); } else { - if (atomic_read(&play->fifo_status.is_busy)) { + if (play->pattern_src == FIFO && + atomic_read(&play->fifo_status.is_busy)) { dev_dbg(chip->dev, "FIFO playing is not done yet, defer stopping in erase\n"); return 0; } @@ -1663,25 +1691,16 @@ static int haptics_erase(struct input_dev *dev, int effect_id) if ((play->pattern_src == FIFO) && atomic_read(&play->fifo_status.is_busy)) { - dev_dbg(chip->dev, "cancelling FIFO playing\n"); - atomic_set(&play->fifo_status.cancelled, 1); + if (atomic_read(&play->fifo_status.written_done) == 0) { + dev_dbg(chip->dev, "cancelling FIFO playing\n"); + atomic_set(&play->fifo_status.cancelled, 1); + } - rc = haptics_enable_play(chip, false); - if (rc < 0) + rc = haptics_stop_fifo_play(chip); + if (rc < 0) { + dev_err(chip->dev, "stop FIFO playing failed, rc=%d\n"); return rc; - - atomic_set(&play->fifo_status.is_busy, 0); - - /* free custom_effect FIFO samples buffer after stopping play */ - kfree(chip->custom_effect->fifo->samples); - chip->custom_effect->fifo->samples = NULL; - - /* restore FIFO play rate back to T_LRA */ - rc = haptics_set_fifo_playrate(chip, T_LRA); - if (rc < 0) - return rc; - - haptics_fifo_empty_irq_config(chip, false); + } } return 0; @@ -1825,24 +1844,24 @@ static irqreturn_t fifo_empty_irq_handler(int irq, void *data) int rc, num; if (atomic_read(&chip->play.fifo_status.written_done) == 1) { - dev_dbg(chip->dev, "FIFO data is done playing\n"); - rc = haptics_enable_play(chip, false); + /* + * Check the FIFO real time fill status before stopping + * play to make sure that all FIFO samples can be played + * successfully. If there are still samples left in FIFO + * memory, defer the stop into erase() function. + */ + num = haptics_get_available_fifo_memory(chip); + if (num != MAX_FIFO_SAMPLES(chip)) { + dev_dbg(chip->dev, "%d FIFO samples still in playing\n", + MAX_FIFO_SAMPLES(chip) - num); + return IRQ_HANDLED; + } + + rc = haptics_stop_fifo_play(chip); if (rc < 0) return IRQ_HANDLED; - /* restore FIFO play rate back to T_LRA */ - rc = haptics_set_fifo_playrate(chip, T_LRA); - if (rc < 0) - return IRQ_HANDLED; - - haptics_fifo_empty_irq_config(chip, false); - - /* free custom_effect FIFO samples after playing is done */ - kfree(chip->custom_effect->fifo->samples); - chip->custom_effect->fifo->samples = NULL; - - atomic_set(&chip->play.fifo_status.written_done, 0); - atomic_set(&chip->play.fifo_status.is_busy, 0); + dev_dbg(chip->dev, "FIFO playing is done\n"); } else { if (atomic_read(&status->cancelled) == 1) { dev_dbg(chip->dev, "FIFO programming got cancelled\n"); From 6caf52bf7a114e3db96d3a8e2d3000d60b4159d8 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 20 May 2020 12:14:43 +0800 Subject: [PATCH 4/4] input: qcom-hv-haptics: adjust T_LRA for auto RC CLK calibration case When auto RC clock calibration mode is selected, the 32.768 KHz sleep clock is used as the reference clock but the hardware module assumes the reference clock is 32 KHz. This results in inaccuracy on output drive frequency. Correct this by scaling the LRA period using a factor of 1.024 in auto RC clock calibration case. Change-Id: I1d4f6cb90c00ffbd9932928e64666a2122d8d4af Signed-off-by: Fenglin Wu --- drivers/input/misc/qcom-hv-haptics.c | 40 ++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 69caba0cad32..ad888829598c 100644 --- a/drivers/input/misc/qcom-hv-haptics.c +++ b/drivers/input/misc/qcom-hv-haptics.c @@ -140,6 +140,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_AUTO_VAL 1 #define CAL_RC_CLK_MANUAL_VAL 2 /* version register definitions for HAPTICS_PATTERN module */ @@ -676,6 +677,26 @@ static int get_brake_play_length_us(struct brake_cfg *brake, u32 t_lra_us) #define V1_CL_TLRA_STEP_AUTO_RES_CAL_NOT_DONE_NS 5000 #define V1_CL_TLRA_STEP_AUTO_RES_CAL_DONE_NS 3333 +#define AUTO_CAL_CLK_SCALE_DEN 1000 +#define AUTO_CAL_CLK_SCALE_NUM 1024 +static int haptics_adjust_lra_period(struct haptics_chip *chip, u32 *t_lra_us) +{ + int rc; + u8 val; + + rc = haptics_read(chip, chip->cfg_addr_base, + HAP_CFG_CAL_EN_REG, &val, 1); + if (rc < 0) + return rc; + + if ((val & CAL_RC_CLK_MASK) == + (CAL_RC_CLK_AUTO_VAL << CAL_RC_CLK_SHIFT)) + *t_lra_us = div_u64((u64)(*t_lra_us) * AUTO_CAL_CLK_SCALE_NUM, + AUTO_CAL_CLK_SCALE_DEN); + + return 0; +} + static int haptics_get_closeloop_lra_period_v1( struct haptics_chip *chip) { @@ -715,7 +736,6 @@ static int haptics_get_closeloop_lra_period_v1( config->cl_t_lra_us = (tmp * step_ns) / 1000; return 0; - } #define CL_TLRA_STEP_NS 1666 @@ -765,6 +785,10 @@ static int haptics_get_closeloop_lra_period(struct haptics_chip *chip) 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); @@ -895,6 +919,7 @@ static int haptics_set_pattern(struct haptics_chip *chip, u8 values[SAMPLES_PER_PATTERN * 3] = { 0 }; u8 ptn_tlra_addr, ptn_cfg_addr; int i, rc, tmp; + u32 play_rate_us; if (src != PATTERN1 && src != PATTERN2) { dev_err(chip->dev, "no pattern src specified!\n"); @@ -908,8 +933,14 @@ static int haptics_set_pattern(struct haptics_chip *chip, ptn_cfg_addr = HAP_PTN_PTRN2_CFG_REG; } + /* Adjust T_LRA before programming it into HW */ + play_rate_us = pattern->play_rate_us; + rc = haptics_adjust_lra_period(chip, &play_rate_us); + if (rc < 0) + return rc; + /* Configure T_LRA for this pattern */ - tmp = pattern->play_rate_us / TLRA_STEP_US; + tmp = play_rate_us / TLRA_STEP_US; values[0] = (tmp >> 8) & TLRA_OL_MSB_MASK; values[1] = tmp & TLRA_OL_LSB_MASK; rc = haptics_write(chip, chip->ptn_addr_base, ptn_tlra_addr, @@ -1753,6 +1784,11 @@ static int haptics_config_openloop_lra_period(struct haptics_chip *chip, { u32 tmp; u8 val[2]; + int rc; + + rc = haptics_adjust_lra_period(chip, &t_lra_us); + if (rc < 0) + return rc; tmp = t_lra_us / TLRA_STEP_US; val[0] = (tmp >> 8) & TLRA_OL_MSB_MASK;