diff --git a/drivers/input/misc/qcom-hv-haptics.c b/drivers/input/misc/qcom-hv-haptics.c index 522608034ada..a3f57d40ed2e 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