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

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
...@@ -367,6 +367,7 @@ static int cyapa_open(struct input_dev *input) ...@@ -367,6 +367,7 @@ static int cyapa_open(struct input_dev *input)
{ {
struct cyapa *cyapa = input_get_drvdata(input); struct cyapa *cyapa = input_get_drvdata(input);
struct i2c_client *client = cyapa->client; struct i2c_client *client = cyapa->client;
struct device *dev = &client->dev;
int error; int error;
error = mutex_lock_interruptible(&cyapa->state_sync_lock); error = mutex_lock_interruptible(&cyapa->state_sync_lock);
...@@ -380,10 +381,9 @@ static int cyapa_open(struct input_dev *input) ...@@ -380,10 +381,9 @@ static int cyapa_open(struct input_dev *input)
* when in operational mode. * when in operational mode.
*/ */
error = cyapa->ops->set_power_mode(cyapa, error = cyapa->ops->set_power_mode(cyapa,
PWR_MODE_FULL_ACTIVE, 0); PWR_MODE_FULL_ACTIVE, 0, false);
if (error) { if (error) {
dev_warn(&client->dev, dev_warn(dev, "set active power failed: %d\n", error);
"set active power failed: %d\n", error);
goto out; goto out;
} }
} else { } else {
...@@ -395,10 +395,14 @@ static int cyapa_open(struct input_dev *input) ...@@ -395,10 +395,14 @@ static int cyapa_open(struct input_dev *input)
} }
enable_irq(client->irq); enable_irq(client->irq);
if (!pm_runtime_enabled(&client->dev)) { if (!pm_runtime_enabled(dev)) {
pm_runtime_set_active(&client->dev); pm_runtime_set_active(dev);
pm_runtime_enable(&client->dev); pm_runtime_enable(dev);
} }
pm_runtime_get_sync(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_sync_autosuspend(dev);
out: out:
mutex_unlock(&cyapa->state_sync_lock); mutex_unlock(&cyapa->state_sync_lock);
return error; return error;
...@@ -408,16 +412,17 @@ static void cyapa_close(struct input_dev *input) ...@@ -408,16 +412,17 @@ static void cyapa_close(struct input_dev *input)
{ {
struct cyapa *cyapa = input_get_drvdata(input); struct cyapa *cyapa = input_get_drvdata(input);
struct i2c_client *client = cyapa->client; struct i2c_client *client = cyapa->client;
struct device *dev = &cyapa->client->dev;
mutex_lock(&cyapa->state_sync_lock); mutex_lock(&cyapa->state_sync_lock);
disable_irq(client->irq); disable_irq(client->irq);
if (pm_runtime_enabled(&client->dev)) if (pm_runtime_enabled(dev))
pm_runtime_disable(&client->dev); pm_runtime_disable(dev);
pm_runtime_set_suspended(&client->dev); pm_runtime_set_suspended(dev);
if (cyapa->operational) 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); mutex_unlock(&cyapa->state_sync_lock);
} }
...@@ -527,7 +532,7 @@ static void cyapa_enable_irq_for_cmd(struct cyapa *cyapa) ...@@ -527,7 +532,7 @@ static void cyapa_enable_irq_for_cmd(struct cyapa *cyapa)
*/ */
if (!input || cyapa->operational) if (!input || cyapa->operational)
cyapa->ops->set_power_mode(cyapa, cyapa->ops->set_power_mode(cyapa,
PWR_MODE_FULL_ACTIVE, 0); PWR_MODE_FULL_ACTIVE, 0, false);
/* Gen3 always using polling mode for command. */ /* Gen3 always using polling mode for command. */
if (cyapa->gen >= CYAPA_GEN5) if (cyapa->gen >= CYAPA_GEN5)
enable_irq(cyapa->client->irq); enable_irq(cyapa->client->irq);
...@@ -542,7 +547,8 @@ static void cyapa_disable_irq_for_cmd(struct cyapa *cyapa) ...@@ -542,7 +547,8 @@ static void cyapa_disable_irq_for_cmd(struct cyapa *cyapa)
if (cyapa->gen >= CYAPA_GEN5) if (cyapa->gen >= CYAPA_GEN5)
disable_irq(cyapa->client->irq); disable_irq(cyapa->client->irq);
if (!input || cyapa->operational) 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) ...@@ -609,7 +615,7 @@ static int cyapa_initialize(struct cyapa *cyapa)
/* Power down the device until we need it. */ /* Power down the device until we need it. */
if (cyapa->operational) 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; return 0;
} }
...@@ -625,7 +631,8 @@ static int cyapa_reinitialize(struct cyapa *cyapa) ...@@ -625,7 +631,8 @@ static int cyapa_reinitialize(struct cyapa *cyapa)
/* Avoid command failures when TP was in OFF state. */ /* Avoid command failures when TP was in OFF state. */
if (cyapa->operational) 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); error = cyapa_detect(cyapa);
if (error) if (error)
...@@ -644,7 +651,8 @@ static int cyapa_reinitialize(struct cyapa *cyapa) ...@@ -644,7 +651,8 @@ static int cyapa_reinitialize(struct cyapa *cyapa)
if (!input || !input->users) { if (!input || !input->users) {
/* Reset to power OFF state to save power when no user open. */ /* Reset to power OFF state to save power when no user open. */
if (cyapa->operational) 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) { } else if (!error && cyapa->operational) {
/* /*
* Make sure only enable runtime PM when device is * Make sure only enable runtime PM when device is
...@@ -652,6 +660,10 @@ static int cyapa_reinitialize(struct cyapa *cyapa) ...@@ -652,6 +660,10 @@ static int cyapa_reinitialize(struct cyapa *cyapa)
*/ */
pm_runtime_set_active(dev); pm_runtime_set_active(dev);
pm_runtime_enable(dev); pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_sync_autosuspend(dev);
} }
return error; return error;
...@@ -661,8 +673,8 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id) ...@@ -661,8 +673,8 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id)
{ {
struct cyapa *cyapa = dev_id; struct cyapa *cyapa = dev_id;
struct device *dev = &cyapa->client->dev; struct device *dev = &cyapa->client->dev;
int error;
pm_runtime_get_sync(dev);
if (device_may_wakeup(dev)) if (device_may_wakeup(dev))
pm_wakeup_event(dev, 0); pm_wakeup_event(dev, 0);
...@@ -681,7 +693,24 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id) ...@@ -681,7 +693,24 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id)
goto out; 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)) { if (!mutex_trylock(&cyapa->state_sync_lock)) {
cyapa->ops->sort_empty_output_data(cyapa, cyapa->ops->sort_empty_output_data(cyapa,
NULL, NULL, NULL); NULL, NULL, NULL);
...@@ -693,8 +722,6 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id) ...@@ -693,8 +722,6 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id)
} }
out: out:
pm_runtime_mark_last_busy(dev);
pm_runtime_put_sync_autosuspend(dev);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -1335,7 +1362,7 @@ static int __maybe_unused cyapa_suspend(struct device *dev) ...@@ -1335,7 +1362,7 @@ static int __maybe_unused cyapa_suspend(struct device *dev)
power_mode = device_may_wakeup(dev) ? cyapa->suspend_power_mode power_mode = device_may_wakeup(dev) ? cyapa->suspend_power_mode
: PWR_MODE_OFF; : PWR_MODE_OFF;
error = cyapa->ops->set_power_mode(cyapa, power_mode, error = cyapa->ops->set_power_mode(cyapa, power_mode,
cyapa->suspend_sleep_time); cyapa->suspend_sleep_time, true);
if (error) if (error)
dev_err(dev, "suspend set power mode failed: %d\n", dev_err(dev, "suspend set power mode failed: %d\n",
error); error);
...@@ -1389,7 +1416,8 @@ static int __maybe_unused cyapa_runtime_suspend(struct device *dev) ...@@ -1389,7 +1416,8 @@ static int __maybe_unused cyapa_runtime_suspend(struct device *dev)
error = cyapa->ops->set_power_mode(cyapa, error = cyapa->ops->set_power_mode(cyapa,
cyapa->runtime_suspend_power_mode, cyapa->runtime_suspend_power_mode,
cyapa->runtime_suspend_sleep_time); cyapa->runtime_suspend_sleep_time,
false);
if (error) if (error)
dev_warn(dev, "runtime suspend failed: %d\n", error); dev_warn(dev, "runtime suspend failed: %d\n", error);
...@@ -1401,7 +1429,8 @@ static int __maybe_unused cyapa_runtime_resume(struct device *dev) ...@@ -1401,7 +1429,8 @@ static int __maybe_unused cyapa_runtime_resume(struct device *dev)
struct cyapa *cyapa = dev_get_drvdata(dev); struct cyapa *cyapa = dev_get_drvdata(dev);
int error; 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) if (error)
dev_warn(dev, "runtime resume failed: %d\n", error); dev_warn(dev, "runtime resume failed: %d\n", error);
......
...@@ -273,7 +273,7 @@ struct cyapa_dev_ops { ...@@ -273,7 +273,7 @@ struct cyapa_dev_ops {
int (*sort_empty_output_data)(struct cyapa *, int (*sort_empty_output_data)(struct cyapa *,
u8 *, int *, cb_sort); 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); int (*set_proximity)(struct cyapa *, bool);
}; };
......
...@@ -950,7 +950,7 @@ static u16 cyapa_get_wait_time_for_pwr_cmd(u8 pwr_mode) ...@@ -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. * 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, 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; int ret;
u8 power; u8 power;
...@@ -1112,7 +1112,7 @@ static int cyapa_gen3_do_operational_check(struct cyapa *cyapa) ...@@ -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. * may cause problems, so we set the power mode first here.
*/ */
error = cyapa_gen3_set_power_mode(cyapa, error = cyapa_gen3_set_power_mode(cyapa,
PWR_MODE_FULL_ACTIVE, 0); PWR_MODE_FULL_ACTIVE, 0, false);
if (error) if (error)
dev_err(dev, "%s: set full power mode failed: %d\n", dev_err(dev, "%s: set full power mode failed: %d\n",
__func__, error); __func__, error);
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <linux/crc-itu-t.h> #include <linux/crc-itu-t.h>
#include <linux/pm_runtime.h>
#include "cyapa.h" #include "cyapa.h"
...@@ -1565,7 +1566,7 @@ int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state) ...@@ -1565,7 +1566,7 @@ int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state)
} }
static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, 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; struct device *dev = &cyapa->client->dev;
u8 power_state; u8 power_state;
...@@ -1574,9 +1575,6 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, ...@@ -1574,9 +1575,6 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
if (cyapa->state != CYAPA_STATE_GEN5_APP) if (cyapa->state != CYAPA_STATE_GEN5_APP)
return 0; 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) { if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
/* /*
* Assume TP in deep sleep mode when driver is loaded, * Assume TP in deep sleep mode when driver is loaded,
...@@ -1679,8 +1677,8 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, ...@@ -1679,8 +1677,8 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
* is suspending which may cause interrupt line unable to be * is suspending which may cause interrupt line unable to be
* asserted again. * asserted again.
*/ */
cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); if (is_suspend)
cyapa_gen5_disable_pip_report(cyapa); cyapa_gen5_disable_pip_report(cyapa);
PIP_DEV_SET_PWR_STATE(cyapa, PIP_DEV_SET_PWR_STATE(cyapa,
cyapa_sleep_time_to_pwr_cmd(sleep_time)); cyapa_sleep_time_to_pwr_cmd(sleep_time));
...@@ -2515,7 +2513,7 @@ static int cyapa_gen5_do_operational_check(struct cyapa *cyapa) ...@@ -2515,7 +2513,7 @@ static int cyapa_gen5_do_operational_check(struct cyapa *cyapa)
* the device state is required. * the device state is required.
*/ */
error = cyapa_gen5_set_power_mode(cyapa, error = cyapa_gen5_set_power_mode(cyapa,
PWR_MODE_FULL_ACTIVE, 0); PWR_MODE_FULL_ACTIVE, 0, false);
if (error) if (error)
dev_warn(dev, "%s: failed to set power active mode.\n", dev_warn(dev, "%s: failed to set power active mode.\n",
__func__); __func__);
...@@ -2757,7 +2755,16 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa) ...@@ -2757,7 +2755,16 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa)
/* /*
* Device wake event from deep sleep mode for touch. * Device wake event from deep sleep mode for touch.
* This interrupt event is used to wake system up. * 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; return 0;
} else if (report_id != PIP_TOUCH_REPORT_ID && } else if (report_id != PIP_TOUCH_REPORT_ID &&
report_id != PIP_BTN_REPORT_ID && report_id != PIP_BTN_REPORT_ID &&
......
...@@ -429,7 +429,7 @@ static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state) ...@@ -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, 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 device *dev = &cyapa->client->dev;
struct gen6_interval_setting *interval_setting = struct gen6_interval_setting *interval_setting =
...@@ -693,7 +693,7 @@ static int cyapa_gen6_operational_check(struct cyapa *cyapa) ...@@ -693,7 +693,7 @@ static int cyapa_gen6_operational_check(struct cyapa *cyapa)
* the device state is required. * the device state is required.
*/ */
error = cyapa_gen6_set_power_mode(cyapa, error = cyapa_gen6_set_power_mode(cyapa,
PWR_MODE_FULL_ACTIVE, 0); PWR_MODE_FULL_ACTIVE, 0, false);
if (error) if (error)
dev_warn(dev, "%s: failed to set power active mode.\n", dev_warn(dev, "%s: failed to set power active mode.\n",
__func__); __func__);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment