All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dudley Du <dudl@cypress.com>
To: "Dmitry Torokhov (dmitry.torokhov@gmail.com)" 
	<dmitry.torokhov@gmail.com>
Cc: Benson Leung <bleung@google.com>,
	Daniel Kurtz <djkurtz@google.com>,
	"linux-input@vger.kernel.org" <linux-input@vger.kernel.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Subject: [PATCH 3/6] input: cyapa: add power mode sleep and runtime power mode supported.
Date: Mon, 14 Apr 2014 07:54:12 +0000	[thread overview]
Message-ID: <77BC725C9062764F874D79F51E1F1A8F40C11442@S04-MBX01-01.s04.local> (raw)

[-- Attachment #1: Type: text/plain, Size: 11211 bytes --]

In order to save power when the trackpad device is not used, the sleep power
mode and runtime power mode must be supported. And the enter sleep time can
be configured in the sysfs system.
TEST=test on Chomebooks.

Signed-off-by: Du, Dudley <dudl@cypress.com>
---
diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
index 7b269d8..6820b3f 100644
--- a/drivers/input/mouse/cyapa.c
+++ b/drivers/input/mouse/cyapa.c
@@ -27,6 +27,7 @@
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 #include <linux/unaligned/access_ok.h>
+#include <linux/pm_runtime.h>

 /* APA trackpad firmware generation */
 #define CYAPA_GEN_UNKNOWN   0x00   /* unknown protocol. */
@@ -505,6 +506,10 @@ struct cyapa {
        u16 suspend_sleep_time;
        u8 real_power_mode;
        u16 real_sleep_time;
+#ifdef CONFIG_PM_RUNTIME
+       u8 runtime_suspend_power_mode;
+       u16 runtime_suspend_sleep_time;
+#endif /* CONFIG_PM_RUNTIME */
        bool suspended;

        /* read from query data region. */
@@ -1873,6 +1878,13 @@ static int cyapa_gen5_bl_exit(struct cyapa *cyapa)
        return -EAGAIN;
 }

+static int cyapa_gen5_sleep_time_check(u16 sleep_time)
+{
+       if (sleep_time > 1000)
+               sleep_time = 1000;
+       return sleep_time;
+}
+
 static int cyapa_gen5_change_power_state(struct cyapa *cyapa, u8 power_state)
 {
        int ret;
@@ -2571,6 +2583,9 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id)
        struct input_dev *input = cyapa->input;
        int length;

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

@@ -2646,6 +2661,7 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id)

        if (cyapa->cyapa_irq_handler)
                cyapa->cyapa_irq_handler(cyapa);
+       pm_runtime_put_sync_autosuspend(dev);

 out:
        return IRQ_HANDLED;
@@ -2945,6 +2961,152 @@ static void cyapa_detect(struct cyapa *cyapa)
                cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
 }

+
+/*
+ * Sysfs Interface.
+ */
+
+#ifdef CONFIG_PM_SLEEP
+static ssize_t cyapa_show_suspend_scanrate(struct device *dev,
+                                          struct device_attribute *attr,
+                                          char *buf)
+{
+       struct cyapa *cyapa = dev_get_drvdata(dev);
+       u8 pwr_cmd = cyapa->suspend_power_mode;
+       u16 sleep_time;
+       int len;
+
+       if (pwr_cmd == PWR_MODE_BTN_ONLY)
+               len = scnprintf(buf, PAGE_SIZE, "%s\n", BTN_ONLY_MODE_NAME);
+       else if (pwr_cmd == PWR_MODE_OFF)
+               len = scnprintf(buf, PAGE_SIZE, "%s\n", OFF_MODE_NAME);
+       else {
+               if (cyapa->gen == CYAPA_GEN3)
+                       sleep_time = cyapa_pwr_cmd_to_sleep_time(pwr_cmd);
+               else
+                       sleep_time = cyapa->suspend_sleep_time;
+               len = scnprintf(buf, PAGE_SIZE, "%u\n", sleep_time);
+       }
+
+       return len;
+}
+
+static ssize_t cyapa_update_suspend_scanrate(struct device *dev,
+                                            struct device_attribute *attr,
+                                            const char *buf, size_t count)
+{
+       struct cyapa *cyapa = dev_get_drvdata(dev);
+       u16 sleep_time;
+
+       if (buf == NULL || count == 0)
+               goto invalidparam;
+
+       if (sysfs_streq(buf, BTN_ONLY_MODE_NAME))
+               cyapa->suspend_power_mode = PWR_MODE_BTN_ONLY;
+       else if (sysfs_streq(buf, OFF_MODE_NAME))
+               cyapa->suspend_power_mode = PWR_MODE_OFF;
+       else if (!kstrtou16(buf, 10, &sleep_time)) {
+               cyapa->suspend_power_mode =
+                               cyapa_sleep_time_to_pwr_cmd(sleep_time);
+               if (cyapa->gen > CYAPA_GEN3)
+                       cyapa->suspend_sleep_time =
+                               cyapa_gen5_sleep_time_check(sleep_time);
+       } else
+               goto invalidparam;
+
+       return count;
+
+invalidparam:
+       dev_err(dev, "invalid suspend scanrate ms parameters\n");
+       return -EINVAL;
+}
+
+static DEVICE_ATTR(suspend_scanrate_ms, S_IRUGO|S_IWUSR,
+                  cyapa_show_suspend_scanrate,
+                  cyapa_update_suspend_scanrate);
+
+static struct attribute *cyapa_power_wakeup_entries[] = {
+       &dev_attr_suspend_scanrate_ms.attr,
+       NULL,
+};
+
+static const struct attribute_group cyapa_power_wakeup_group = {
+       .name = power_group_name,
+       .attrs = cyapa_power_wakeup_entries,
+};
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_PM_RUNTIME
+static ssize_t cyapa_show_rt_suspend_scanrate(struct device *dev,
+                                             struct device_attribute *attr,
+                                             char *buf)
+{
+       struct cyapa *cyapa = dev_get_drvdata(dev);
+       u8 pwr_cmd = cyapa->runtime_suspend_power_mode;
+
+       if (cyapa->gen == CYAPA_GEN3)
+               return scnprintf(buf, PAGE_SIZE, "%u\n",
+                       cyapa_pwr_cmd_to_sleep_time(pwr_cmd));
+       else
+               return scnprintf(buf, PAGE_SIZE, "%u\n",
+                       cyapa->runtime_suspend_sleep_time);
+}
+
+static ssize_t cyapa_update_rt_suspend_scanrate(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf, size_t count)
+{
+       struct cyapa *cyapa = dev_get_drvdata(dev);
+       u16 time;
+
+       if (buf == NULL || count == 0 || kstrtou16(buf, 10, &time)) {
+               dev_err(dev, "invalid runtime suspend scanrate ms parameter\n");
+               return -EINVAL;
+       }
+
+       /*
+        * When the suspend scanrate is changed, pm_runtime_get to resume
+        * a potentially suspended device, update to the new pwr_cmd
+        * and then pm_runtime_put to suspend into the new power mode.
+        */
+       pm_runtime_get_sync(dev);
+       cyapa->runtime_suspend_power_mode = cyapa_sleep_time_to_pwr_cmd(time);
+       if (cyapa->gen > CYAPA_GEN3)
+               cyapa->runtime_suspend_sleep_time = time;
+       pm_runtime_put_sync_autosuspend(dev);
+       return count;
+}
+
+static DEVICE_ATTR(runtime_suspend_scanrate_ms, S_IRUGO|S_IWUSR,
+                  cyapa_show_rt_suspend_scanrate,
+                  cyapa_update_rt_suspend_scanrate);
+
+static struct attribute *cyapa_power_runtime_entries[] = {
+       &dev_attr_runtime_suspend_scanrate_ms.attr,
+       NULL,
+};
+
+static const struct attribute_group cyapa_power_runtime_group = {
+       .name = power_group_name,
+       .attrs = cyapa_power_runtime_entries,
+};
+
+static void cyapa_start_runtime(struct cyapa *cyapa)
+{
+       struct device *dev = &cyapa->client->dev;
+
+       cyapa->runtime_suspend_power_mode = PWR_MODE_IDLE;
+       if (sysfs_merge_group(&dev->kobj, &cyapa_power_runtime_group))
+               dev_warn(dev, "error creating wakeup runtime entries.\n");
+       pm_runtime_set_active(dev);
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_DELAY);
+       pm_runtime_enable(dev);
+}
+#else
+static void cyapa_start_runtime(struct cyapa *cyapa) {}
+#endif /* CONFIG_PM_RUNTIME */
+
 static void cyapa_detect_async(void *data, async_cookie_t cookie)
 {
        struct cyapa *cyapa = (struct cyapa *)data;
@@ -2957,6 +3119,15 @@ static void cyapa_detect_async(void *data, async_cookie_t cookie)
        atomic_dec(&cyapa->in_detecting);
 }

+static void cyapa_detect_and_start(void *data, async_cookie_t cookie)
+{
+       struct cyapa *cyapa = data;
+
+       cyapa_detect_async(data, cookie);
+
+       cyapa_start_runtime(cyapa);
+}
+
 static int cyapa_probe(struct i2c_client *client,
                       const struct i2c_device_id *dev_id)
 {
@@ -3017,7 +3188,13 @@ static int cyapa_probe(struct i2c_client *client,
        }
        cyapa_disable_irq(cyapa);

-       async_schedule(cyapa_detect_async, cyapa);
+#ifdef CONFIG_PM_SLEEP
+       if (device_can_wakeup(dev) &&
+           sysfs_merge_group(&client->dev.kobj, &cyapa_power_wakeup_group))
+               dev_warn(dev, "error creating wakeup power entries.\n");
+#endif /* CONFIG_PM_SLEEP */
+
+       async_schedule(cyapa_detect_and_start, cyapa);
        return 0;

 err_unregister_device:
@@ -3032,6 +3209,16 @@ static int cyapa_remove(struct i2c_client *client)
 {
        struct cyapa *cyapa = i2c_get_clientdata(client);

+       pm_runtime_disable(&client->dev);
+
+#ifdef CONFIG_PM_SLEEP
+       sysfs_unmerge_group(&client->dev.kobj, &cyapa_power_wakeup_group);
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+       sysfs_unmerge_group(&client->dev.kobj, &cyapa_power_runtime_group);
+#endif
+
        free_irq(cyapa->irq, cyapa);
        input_unregister_device(cyapa->input);

@@ -3090,12 +3277,56 @@ static int cyapa_resume(struct device *dev)

        async_schedule(cyapa_detect_async, cyapa);

+       /* runtime set active to reflect active state. */
+       pm_runtime_disable(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
        cyapa->suspended = false;
        return 0;
 }
 #endif /* CONFIG_PM_SLEEP */

-static SIMPLE_DEV_PM_OPS(cyapa_pm_ops, cyapa_suspend, cyapa_resume);
+#ifdef CONFIG_PM_RUNTIME
+static int cyapa_runtime_suspend(struct device *dev)
+{
+       int ret;
+       struct cyapa *cyapa = dev_get_drvdata(dev);
+
+       if (cyapa->cyapa_set_power_mode) {
+               /* set trackpad device to idle mode */
+               ret = cyapa->cyapa_set_power_mode(cyapa,
+                               cyapa->runtime_suspend_power_mode,
+                               cyapa->runtime_suspend_sleep_time);
+               if (ret)
+                       dev_err(dev, "runtime suspend failed, %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int cyapa_runtime_resume(struct device *dev)
+{
+       int ret;
+       struct cyapa *cyapa = dev_get_drvdata(dev);
+
+       if (cyapa->cyapa_set_power_mode) {
+               /* resume to full active mode */
+               ret = cyapa->cyapa_set_power_mode(cyapa,
+                               PWR_MODE_FULL_ACTIVE, 0);
+               if (ret)
+                       dev_err(dev, "runtime resume failed, %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+static const struct dev_pm_ops cyapa_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(cyapa_suspend, cyapa_resume)
+       SET_RUNTIME_PM_OPS(cyapa_runtime_suspend, cyapa_runtime_resume, NULL)
+};

 static const struct i2c_device_id cyapa_id_table[] = {
        { "cyapa", 0 },
This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.

[-- Attachment #2: winmail.dat --]
[-- Type: application/ms-tnef, Size: 7477 bytes --]

             reply	other threads:[~2014-04-14  8:01 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-04-14  7:54 Dudley Du [this message]
2014-04-14  7:54 ` [PATCH 3/6] input: cyapa: add power mode sleep and runtime power mode supported Dudley Du
2014-04-16  8:38 Dudley Du
2014-04-16  8:38 ` Dudley Du

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=77BC725C9062764F874D79F51E1F1A8F40C11442@S04-MBX01-01.s04.local \
    --to=dudl@cypress.com \
    --cc=bleung@google.com \
    --cc=djkurtz@google.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.