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

Commit 757cae5a authored by Dudley Du's avatar Dudley Du Committed by Dmitry Torokhov
Browse files

Input: cyapa - fully support runtime suspend power management



Fix the the runtime suspend power management not working issue when system
starts up and before user touches the trackpad device.
TEST=test on Chromebook.

Signed-off-by: default avatarDudley Du <dudl@cypress.com>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 945525ee
Loading
Loading
Loading
Loading
+51 −22
Original line number Diff line number Diff line
@@ -367,6 +367,7 @@ static int cyapa_open(struct input_dev *input)
{
	struct cyapa *cyapa = input_get_drvdata(input);
	struct i2c_client *client = cyapa->client;
	struct device *dev = &client->dev;
	int error;

	error = mutex_lock_interruptible(&cyapa->state_sync_lock);
@@ -380,10 +381,9 @@ static int cyapa_open(struct input_dev *input)
		 * when in operational mode.
		 */
		error = cyapa->ops->set_power_mode(cyapa,
				PWR_MODE_FULL_ACTIVE, 0);
				PWR_MODE_FULL_ACTIVE, 0, false);
		if (error) {
			dev_warn(&client->dev,
				"set active power failed: %d\n", error);
			dev_warn(dev, "set active power failed: %d\n", error);
			goto out;
		}
	} else {
@@ -395,10 +395,14 @@ static int cyapa_open(struct input_dev *input)
	}

	enable_irq(client->irq);
	if (!pm_runtime_enabled(&client->dev)) {
		pm_runtime_set_active(&client->dev);
		pm_runtime_enable(&client->dev);
	if (!pm_runtime_enabled(dev)) {
		pm_runtime_set_active(dev);
		pm_runtime_enable(dev);
	}

	pm_runtime_get_sync(dev);
	pm_runtime_mark_last_busy(dev);
	pm_runtime_put_sync_autosuspend(dev);
out:
	mutex_unlock(&cyapa->state_sync_lock);
	return error;
@@ -408,16 +412,17 @@ static void cyapa_close(struct input_dev *input)
{
	struct cyapa *cyapa = input_get_drvdata(input);
	struct i2c_client *client = cyapa->client;
	struct device *dev = &cyapa->client->dev;

	mutex_lock(&cyapa->state_sync_lock);

	disable_irq(client->irq);
	if (pm_runtime_enabled(&client->dev))
		pm_runtime_disable(&client->dev);
	pm_runtime_set_suspended(&client->dev);
	if (pm_runtime_enabled(dev))
		pm_runtime_disable(dev);
	pm_runtime_set_suspended(dev);

	if (cyapa->operational)
		cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0);
		cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false);

	mutex_unlock(&cyapa->state_sync_lock);
}
@@ -527,7 +532,7 @@ static void cyapa_enable_irq_for_cmd(struct cyapa *cyapa)
		 */
		if (!input || cyapa->operational)
			cyapa->ops->set_power_mode(cyapa,
				PWR_MODE_FULL_ACTIVE, 0);
				PWR_MODE_FULL_ACTIVE, 0, false);
		/* Gen3 always using polling mode for command. */
		if (cyapa->gen >= CYAPA_GEN5)
			enable_irq(cyapa->client->irq);
@@ -542,7 +547,8 @@ static void cyapa_disable_irq_for_cmd(struct cyapa *cyapa)
		if (cyapa->gen >= CYAPA_GEN5)
			disable_irq(cyapa->client->irq);
		if (!input || cyapa->operational)
			cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0);
			cyapa->ops->set_power_mode(cyapa,
						   PWR_MODE_OFF, 0, false);
	}
}

@@ -609,7 +615,7 @@ static int cyapa_initialize(struct cyapa *cyapa)

	/* Power down the device until we need it. */
	if (cyapa->operational)
		cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0);
		cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false);

	return 0;
}
@@ -625,7 +631,8 @@ static int cyapa_reinitialize(struct cyapa *cyapa)

	/* Avoid command failures when TP was in OFF state. */
	if (cyapa->operational)
		cyapa->ops->set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE, 0);
		cyapa->ops->set_power_mode(cyapa,
					   PWR_MODE_FULL_ACTIVE, 0, false);

	error = cyapa_detect(cyapa);
	if (error)
@@ -644,7 +651,8 @@ static int cyapa_reinitialize(struct cyapa *cyapa)
	if (!input || !input->users) {
		/* Reset to power OFF state to save power when no user open. */
		if (cyapa->operational)
			cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0);
			cyapa->ops->set_power_mode(cyapa,
						   PWR_MODE_OFF, 0, false);
	} else if (!error && cyapa->operational) {
		/*
		 * Make sure only enable runtime PM when device is
@@ -652,6 +660,10 @@ static int cyapa_reinitialize(struct cyapa *cyapa)
		 */
		pm_runtime_set_active(dev);
		pm_runtime_enable(dev);

		pm_runtime_get_sync(dev);
		pm_runtime_mark_last_busy(dev);
		pm_runtime_put_sync_autosuspend(dev);
	}

	return error;
@@ -661,8 +673,8 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id)
{
	struct cyapa *cyapa = dev_id;
	struct device *dev = &cyapa->client->dev;
	int error;

	pm_runtime_get_sync(dev);
	if (device_may_wakeup(dev))
		pm_wakeup_event(dev, 0);

@@ -681,7 +693,24 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id)
			goto out;
		}

		if (!cyapa->operational || cyapa->ops->irq_handler(cyapa)) {
		if (cyapa->operational) {
			error = cyapa->ops->irq_handler(cyapa);

			/*
			 * Apply runtime power management to touch report event
			 * except the events caused by the command responses.
			 * Note:
			 * It will introduce about 20~40 ms additional delay
			 * time in receiving for first valid touch report data.
			 * The time is used to execute device runtime resume
			 * process.
			 */
			pm_runtime_get_sync(dev);
			pm_runtime_mark_last_busy(dev);
			pm_runtime_put_sync_autosuspend(dev);
		}

		if (!cyapa->operational || error) {
			if (!mutex_trylock(&cyapa->state_sync_lock)) {
				cyapa->ops->sort_empty_output_data(cyapa,
					NULL, NULL, NULL);
@@ -693,8 +722,6 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id)
	}

out:
	pm_runtime_mark_last_busy(dev);
	pm_runtime_put_sync_autosuspend(dev);
	return IRQ_HANDLED;
}

@@ -1335,7 +1362,7 @@ static int __maybe_unused cyapa_suspend(struct device *dev)
		power_mode = device_may_wakeup(dev) ? cyapa->suspend_power_mode
						    : PWR_MODE_OFF;
		error = cyapa->ops->set_power_mode(cyapa, power_mode,
				cyapa->suspend_sleep_time);
				cyapa->suspend_sleep_time, true);
		if (error)
			dev_err(dev, "suspend set power mode failed: %d\n",
					error);
@@ -1389,7 +1416,8 @@ static int __maybe_unused cyapa_runtime_suspend(struct device *dev)

	error = cyapa->ops->set_power_mode(cyapa,
			cyapa->runtime_suspend_power_mode,
			cyapa->runtime_suspend_sleep_time);
			cyapa->runtime_suspend_sleep_time,
			false);
	if (error)
		dev_warn(dev, "runtime suspend failed: %d\n", error);

@@ -1401,7 +1429,8 @@ static int __maybe_unused cyapa_runtime_resume(struct device *dev)
	struct cyapa *cyapa = dev_get_drvdata(dev);
	int error;

	error = cyapa->ops->set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE, 0);
	error = cyapa->ops->set_power_mode(cyapa,
					   PWR_MODE_FULL_ACTIVE, 0, false);
	if (error)
		dev_warn(dev, "runtime resume failed: %d\n", error);

+1 −1
Original line number Diff line number Diff line
@@ -273,7 +273,7 @@ struct cyapa_dev_ops {
	int (*sort_empty_output_data)(struct cyapa *,
			u8 *, int *, cb_sort);

	int (*set_power_mode)(struct cyapa *, u8, u16);
	int (*set_power_mode)(struct cyapa *, u8, u16, bool);

	int (*set_proximity)(struct cyapa *, bool);
};
+2 −2
Original line number Diff line number Diff line
@@ -950,7 +950,7 @@ static u16 cyapa_get_wait_time_for_pwr_cmd(u8 pwr_mode)
 * Device power mode can only be set when device is in operational mode.
 */
static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode,
				     u16 always_unused)
		u16 always_unused, bool is_suspend_unused)
{
	int ret;
	u8 power;
@@ -1112,7 +1112,7 @@ static int cyapa_gen3_do_operational_check(struct cyapa *cyapa)
		 * may cause problems, so we set the power mode first here.
		 */
		error = cyapa_gen3_set_power_mode(cyapa,
				PWR_MODE_FULL_ACTIVE, 0);
				PWR_MODE_FULL_ACTIVE, 0, false);
		if (error)
			dev_err(dev, "%s: set full power mode failed: %d\n",
				__func__, error);
+14 −7
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <linux/crc-itu-t.h>
#include <linux/pm_runtime.h>
#include "cyapa.h"


@@ -1565,7 +1566,7 @@ int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state)
}

static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
		u8 power_mode, u16 sleep_time)
		u8 power_mode, u16 sleep_time, bool is_suspend)
{
	struct device *dev = &cyapa->client->dev;
	u8 power_state;
@@ -1574,9 +1575,6 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
	if (cyapa->state != CYAPA_STATE_GEN5_APP)
		return 0;

	/* Dump all the report data before do power mode commmands. */
	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);

	if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
		/*
		 * Assume TP in deep sleep mode when driver is loaded,
@@ -1679,7 +1677,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
		 * is suspending which may cause interrupt line unable to be
		 * asserted again.
		 */
		cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
		if (is_suspend)
			cyapa_gen5_disable_pip_report(cyapa);

		PIP_DEV_SET_PWR_STATE(cyapa,
@@ -2515,7 +2513,7 @@ static int cyapa_gen5_do_operational_check(struct cyapa *cyapa)
		 * the device state is required.
		 */
		error = cyapa_gen5_set_power_mode(cyapa,
				PWR_MODE_FULL_ACTIVE, 0);
				PWR_MODE_FULL_ACTIVE, 0, false);
		if (error)
			dev_warn(dev, "%s: failed to set power active mode.\n",
				__func__);
@@ -2757,7 +2755,16 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa)
		/*
		 * Device wake event from deep sleep mode for touch.
		 * This interrupt event is used to wake system up.
		 *
		 * Note:
		 * It will introduce about 20~40 ms additional delay
		 * time in receiving for first valid touch report data.
		 * The time is used to execute device runtime resume
		 * process.
		 */
		pm_runtime_get_sync(dev);
		pm_runtime_mark_last_busy(dev);
		pm_runtime_put_sync_autosuspend(dev);
		return 0;
	} else if (report_id != PIP_TOUCH_REPORT_ID &&
			report_id != PIP_BTN_REPORT_ID &&
+2 −2
Original line number Diff line number Diff line
@@ -429,7 +429,7 @@ static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state)
}

static int cyapa_gen6_set_power_mode(struct cyapa *cyapa,
		u8 power_mode, u16 sleep_time)
		u8 power_mode, u16 sleep_time, bool is_suspend)
{
	struct device *dev = &cyapa->client->dev;
	struct gen6_interval_setting *interval_setting =
@@ -693,7 +693,7 @@ static int cyapa_gen6_operational_check(struct cyapa *cyapa)
		 * the device state is required.
		 */
		error = cyapa_gen6_set_power_mode(cyapa,
				PWR_MODE_FULL_ACTIVE, 0);
				PWR_MODE_FULL_ACTIVE, 0, false);
		if (error)
			dev_warn(dev, "%s: failed to set power active mode.\n",
				__func__);