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: Dudley Du <dudl@cypress.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
This commit is contained in:
parent
945525ee60
commit
757cae5a6f
|
@ -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)
|
||||||
* 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
/* 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)
|
||||||
|
|
||||||
/* 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)
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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 {
|
||||||
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)
|
||||||
* 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)
|
||||||
* 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 @@
|
||||||
#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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
* 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)
|
||||||
* 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)
|
||||||
/*
|
/*
|
||||||
* 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
* 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__);
|
||||||
|
|
Loading…
Reference in New Issue