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 <fenglinw@codeaurora.org>
This commit is contained in:
Fenglin Wu 2020-05-01 19:31:10 +08:00 committed by David Collins
parent 1af05bc261
commit 8dce088a14

View File

@ -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, &amplitude, 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