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

Commit d79818af authored by Fenglin Wu's avatar Fenglin Wu
Browse files

input: qcom-hv-haptics: update FIFO samples in IRQ thread



Instead of sending a completion from the IRQ thread to notify a
worker thread to update the FIFO samples, update the samples in
the IRQ thread directly. This should help achieve lower latency
which is good for FIFO playing.

Meanwhile, following changes are made for FIFO playing:
 1) Defer stopping command into erase() function if it's coming
    before all FIFO samples are played. This gives the hardware
    a little more time to play before stopping.
 2) Restore FIFO play rate to T_LRA after playing is done according
    to hardware recommendation.

Change-Id: I7b21589641d753d45b1261ce0e342373c249f4e5
Signed-off-by: default avatarFenglin Wu <fenglinw@codeaurora.org>
parent afe365c9
Loading
Loading
Loading
Loading
+135 −101
Original line number Diff line number Diff line
@@ -326,11 +326,23 @@ struct haptics_effect {
	bool			auto_res_disable;
};

/**
 * struct fifo_play_status - Data used for recording the FIFO playing status
 *
 * @samples_written:	The number of the samples that has been written into
 *			FIFO memory.
 * @written_done:	The flag to indicate if all of the FIFO samples has
 *			been written to the FIFO memory.
 * @is_busy:		The flag to indicate if it's in the middle of FIFO
 *			playing.
 * @cancelled:		The flag to indicate if FIFO playing is cancelled due
 *			to a stopping command arrived in the middle of playing.
 */
struct fifo_play_status {
	struct completion	fifo_ready;
	u32			samples_written;
	atomic_t		written_done;
	atomic_t		is_busy;
	atomic_t		cancelled;
};

struct haptics_play_info {
@@ -359,9 +371,9 @@ struct haptics_chip {
	struct haptics_hw_config	config;
	struct haptics_effect		*effects;
	struct haptics_play_info	play;
	struct work_struct		fifo_work;
	struct dentry			*debugfs_dir;
	struct regulator_dev		*swr_slave_rdev;
	struct mutex			irq_lock;
	int				fifo_empty_irq;
	u32				effects_count;
	u32				cfg_addr_base;
@@ -871,7 +883,6 @@ static int haptics_get_available_fifo_memory(struct haptics_chip *chip)
	}

	available = MAX_FIFO_SAMPLES(chip) - fill;
	dev_dbg(chip->dev, "Available FIFO memory: %d bytes\n", available);
	return available;
}

@@ -909,12 +920,46 @@ static int haptics_update_fifo_samples(struct haptics_chip *chip,
	return 0;
}

static int haptics_set_fifo_playrate(struct haptics_chip *chip,
				enum s_period period_per_s)
{
	int rc;
	u8 reg;

	reg = (chip->ptn_revision == HAP_PTN_V1) ?
		HAP_PTN_V1_FIFO_PLAY_RATE_REG : HAP_PTN_V2_FIFO_PLAY_RATE_REG;
	rc = haptics_masked_write(chip, chip->ptn_addr_base,
			reg, FIFO_PLAY_RATE_MASK, period_per_s);
	if (rc < 0)
		dev_err(chip->dev, "Set FIFO play rate failed, rc=%d\n", rc);

	return rc;
}

static int haptics_set_fifo_empty_threshold(struct haptics_chip *chip,
							u32 thresh)
{
	u8 reg, thresh_per_bit;
	int rc;

	reg = (chip->ptn_revision == HAP_PTN_V1) ?
		HAP_PTN_V1_FIFO_EMPTY_CFG_REG : HAP_PTN_V2_FIFO_EMPTY_CFG_REG;
	thresh_per_bit = (chip->ptn_revision == HAP_PTN_V1) ?
		HAP_PTN_V1_FIFO_THRESH_LSB : HAP_PTN_V2_FIFO_THRESH_LSB;
	rc = haptics_masked_write(chip, chip->ptn_addr_base,
			reg, EMPTY_THRESH_MASK, thresh / thresh_per_bit);
	if (rc < 0)
		dev_err(chip->dev, "Set FIFO empty threshold failed, rc=%d\n",
				rc);

	return rc;
}

static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo)
{
	struct fifo_play_status *status = &chip->play.fifo_status;
	u32 num, fifo_thresh;
	int rc, thresh_per_bit, available;
	u8 reg;
	int rc, available;

	if (atomic_read(&status->is_busy) == 1) {
		dev_err(chip->dev, "FIFO is busy\n");
@@ -922,14 +967,12 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo)
	}

	/* Configure FIFO play rate */
	reg = (chip->ptn_revision == HAP_PTN_V1) ?
		HAP_PTN_V1_FIFO_PLAY_RATE_REG : HAP_PTN_V2_FIFO_PLAY_RATE_REG;
	rc = haptics_masked_write(chip, chip->ptn_addr_base,
			reg, FIFO_PLAY_RATE_MASK, fifo->period_per_s);
	rc = haptics_set_fifo_playrate(chip, fifo->period_per_s);
	if (rc < 0)
		return rc;

	atomic_set(&status->written_done, 0);
	atomic_set(&status->cancelled, 0);
	status->samples_written = 0;

	/*
@@ -955,30 +998,17 @@ static int haptics_set_fifo(struct haptics_chip *chip, struct fifo_cfg *fifo)
		fifo_thresh = 0;
		atomic_set(&status->written_done, 1);
	} else {
		reinit_completion(&status->fifo_ready);
		fifo_thresh = chip->config.fifo_empty_thresh;
	}

	/*
	 * Set FIFO empty threshold and enable FIFO empty IRQ,
	 * more data can be written into FIFO memory after
	 * the IRQ is triggered.
	 * Set FIFO empty threshold here. FIFO empty IRQ will
	 * be enabled after playing FIFO samples so that more
	 * FIFO samples can be written (if available) when
	 * FIFO empty IRQ is triggered.
	 */
	reg = (chip->ptn_revision == HAP_PTN_V1) ?
		HAP_PTN_V1_FIFO_EMPTY_CFG_REG : HAP_PTN_V2_FIFO_EMPTY_CFG_REG;
	thresh_per_bit = (chip->ptn_revision == HAP_PTN_V1) ?
		HAP_PTN_V1_FIFO_THRESH_LSB : HAP_PTN_V2_FIFO_THRESH_LSB;
	rc = haptics_masked_write(chip, chip->ptn_addr_base,
			reg, EMPTY_THRESH_MASK, fifo_thresh / thresh_per_bit);
	if (rc < 0)
		return rc;

	if (!chip->fifo_empty_irq_en) {
		enable_irq(chip->fifo_empty_irq);
		chip->fifo_empty_irq_en = true;
	}

	return 0;
	return haptics_set_fifo_empty_threshold(chip, fifo_thresh);
}

static int haptics_set_direct_play(struct haptics_chip *chip)
@@ -1167,6 +1197,20 @@ static int haptics_upload_effect(struct input_dev *dev,
	return 0;
}

static void haptics_fifo_empty_irq_config(struct haptics_chip *chip,
						bool enable)
{
	mutex_lock(&chip->irq_lock);
	if (!chip->fifo_empty_irq_en && enable) {
		enable_irq(chip->fifo_empty_irq);
		chip->fifo_empty_irq_en = true;
	} else if (chip->fifo_empty_irq_en && !enable) {
		disable_irq_nosync(chip->fifo_empty_irq);
		chip->fifo_empty_irq_en = false;
	}
	mutex_unlock(&chip->irq_lock);
}

static int haptics_playback(struct input_dev *dev, int effect_id, int val)
{
	struct haptics_chip *chip = input_get_drvdata(dev);
@@ -1179,25 +1223,45 @@ static int haptics_playback(struct input_dev *dev, int effect_id, int val)
		if (rc < 0)
			return rc;

		if ((atomic_read(&play->fifo_status.written_done) == 0)
				&& play->pattern_src == FIFO)
			schedule_work(&chip->fifo_work);
		if (play->pattern_src == FIFO)
			haptics_fifo_empty_irq_config(chip, true);
	} else {
		rc = haptics_enable_play(chip, false);
		if (rc < 0)
			return rc;

		if (chip->fifo_empty_irq_en) {
			disable_irq_nosync(chip->fifo_empty_irq);
			chip->fifo_empty_irq_en = false;
		if (atomic_read(&play->fifo_status.is_busy)) {
			dev_dbg(chip->dev, "FIFO playing is not done yet, defer stopping in erase\n");
			return 0;
		}

		rc = haptics_enable_play(chip, false);
	}

	return 0;
	return rc;
}

static int haptics_erase(struct input_dev *dev, int effect_id)
{
	struct haptics_chip *chip = input_get_drvdata(dev);
	struct haptics_play_info *play = &chip->play;
	int rc;

	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);

		rc = haptics_enable_play(chip, false);
		if (rc < 0)
			return rc;

		atomic_set(&play->fifo_status.is_busy, 0);

		/* 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;
}

@@ -1284,90 +1348,60 @@ static int haptics_hw_init(struct haptics_chip *chip)
	return rc;
}

static void update_fifo_work(struct work_struct *work)
static irqreturn_t fifo_empty_irq_handler(int irq, void *data)
{
	struct haptics_chip *chip = container_of(work,
			struct haptics_chip, fifo_work);
	struct haptics_chip *chip = data;
	struct fifo_cfg *fifo = chip->play.effect->fifo;
	struct fifo_play_status *status = &chip->play.fifo_status;
	u32 samples_written, samples_left;
	u32 samples_left;
	u8 *samples;
	u8 reg;
	int rc, num;

	samples_written = status->samples_written;
	samples_left = fifo->num_s - samples_written;
	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)
			return IRQ_HANDLED;

	while (samples_left > 0) {
		/* Waiting on FIFO empty IRQ triggered */
		rc = wait_for_completion_timeout(&status->fifo_ready,
				msecs_to_jiffies(FIFO_READY_TIMEOUT_MS));
		if (!rc) {
			dev_err(chip->dev, "Timeout on waiting FIFO ready!\n");
			return;
		/* 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);

		atomic_set(&chip->play.fifo_status.written_done, 0);
		atomic_set(&chip->play.fifo_status.is_busy, 0);
	} else {
		if (atomic_read(&status->cancelled) == 1) {
			dev_dbg(chip->dev, "FIFO programming got cancelled\n");
			return IRQ_HANDLED;
		}

		samples_left = fifo->num_s - status->samples_written;
		num = haptics_get_available_fifo_memory(chip);
		if (num < 0)
			return;
			return IRQ_HANDLED;

		if (samples_left <= num)
			num = samples_left;
		else
			reinit_completion(&status->fifo_ready);

		samples = fifo->samples + samples_written;
		samples = fifo->samples + status->samples_written;

		/* Write more pattern data into FIFO memory. */
		rc = haptics_update_fifo_samples(chip, samples, num);
		if (rc < 0) {
			dev_err(chip->dev, "Update FIFO samples failed in fifo_work, rc=%d\n",
			dev_err(chip->dev, "Update FIFO samples failed, rc=%d\n",
					rc);
			return;
		}

		samples_written += num;
		samples_left -= num;
		dev_dbg(chip->dev, "FIFO %d samples written, %d samples left\n",
				samples_written, samples_left);
			return IRQ_HANDLED;
		}

	/*
	 * If all pattern data is written, set FIFO empty
	 * threshold to 0 so that FIFO empty IRQ can be used
	 * for detecting FIFO playing done event.
	 */
	dev_dbg(chip->dev, "FIFO programmed done\n");
		status->samples_written += num;
		if (status->samples_written == fifo->num_s) {
			dev_dbg(chip->dev, "FIFO programming is done\n");
			atomic_set(&chip->play.fifo_status.written_done, 1);
	reg = (chip->ptn_revision == HAP_PTN_V1) ?
		HAP_PTN_V1_FIFO_EMPTY_CFG_REG : HAP_PTN_V2_FIFO_EMPTY_CFG_REG;
	rc = haptics_masked_write(chip, chip->ptn_addr_base,
			reg, EMPTY_THRESH_MASK, 0);
	if (rc < 0)
		dev_err(chip->dev, "set FIFO empty threshold to 0 failed, rc=%d\n",
				rc);
			haptics_set_fifo_empty_threshold(chip, 0);
		}

static irqreturn_t fifo_empty_irq_handler(int irq, void *data)
{
	struct haptics_chip *chip = data;
	int rc;

	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)
			return IRQ_HANDLED;

		if (chip->fifo_empty_irq_en) {
			disable_irq_nosync(chip->fifo_empty_irq);
			chip->fifo_empty_irq_en = false;
		}

		atomic_set(&chip->play.fifo_status.written_done, 0);
		atomic_set(&chip->play.fifo_status.is_busy, 0);
	} else {
		complete(&chip->play.fifo_status.fifo_ready);
	}

	return IRQ_HANDLED;
@@ -2585,13 +2619,13 @@ static int haptics_probe(struct platform_device *pdev)
		return rc;
	}

	mutex_init(&chip->irq_lock);
	disable_irq_nosync(chip->fifo_empty_irq);
	chip->fifo_empty_irq_en = false;

	init_completion(&chip->play.fifo_status.fifo_ready);
	atomic_set(&chip->play.fifo_status.is_busy, 0);
	atomic_set(&chip->play.fifo_status.written_done, 0);
	INIT_WORK(&chip->fifo_work, update_fifo_work);
	atomic_set(&chip->play.fifo_status.cancelled, 0);
	input_dev->name = "qcom-hv-haptics";
	input_set_drvdata(input_dev, chip);
	chip->input_dev = input_dev;