Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1e6d5a81 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "input: qcom-hv-haptics: adjust T_LRA for auto RC CLK calibration case"

parents 832413cf 6caf52bf
Loading
Loading
Loading
Loading
+418 −87
Original line number Diff line number Diff line
@@ -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
@@ -131,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 */
@@ -172,6 +182,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 +375,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 +412,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;
@@ -489,8 +503,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);
@@ -661,8 +675,28 @@ 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
#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)
{
@@ -694,15 +728,14 @@ 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;

	return 0;

}

#define CL_TLRA_STEP_NS				1666
@@ -752,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);

@@ -778,6 +815,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 +849,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;
@@ -850,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");
@@ -863,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,
@@ -1010,8 +1086,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 +1257,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 +1473,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 +1494,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 +1546,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 +1576,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);
@@ -1557,6 +1660,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);
@@ -1572,7 +1702,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;
		}
@@ -1591,25 +1722,16 @@ static int haptics_erase(struct input_dev *dev, int effect_id)

	if ((play->pattern_src == FIFO) &&
			atomic_read(&play->fifo_status.is_busy)) {
		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)
			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)
		rc = haptics_stop_fifo_play(chip);
		if (rc < 0) {
			dev_err(chip->dev, "stop FIFO playing failed, rc=%d\n");
			return rc;

		haptics_fifo_empty_irq_config(chip, false);
		}
	}

	return 0;
@@ -1657,12 +1779,31 @@ 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];
	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;
	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 +1823,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 +1841,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;

@@ -1743,24 +1880,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);
		if (rc < 0)
		/*
		 * 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;
		}

		/* restore FIFO play rate back to T_LRA */
		rc = haptics_set_fifo_playrate(chip, T_LRA);
		rc = haptics_stop_fifo_play(chip);
		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");
@@ -1976,7 +2113,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)
@@ -1994,7 +2131,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);
@@ -2010,7 +2147,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);
@@ -2033,7 +2170,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;
@@ -2042,7 +2179,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;
@@ -2100,7 +2237,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)
@@ -2245,6 +2382,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;
@@ -2291,7 +2472,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)
@@ -2354,6 +2535,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;
@@ -2971,6 +3162,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 +3349,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 +3392,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 +3415,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