From: "Arve Hjønnevåg" <arve@android.com>
To: linux-pm@lists.linux-foundation.org
Cc: ncunningham@crca.org.au, u.luckas@road.de, swetland@google.com
Subject: [PATCH 06/10] PM: Add early suspend api.
Date: Tue, 10 Feb 2009 17:49:11 -0800 [thread overview]
Message-ID: <1234316955-31304-7-git-send-email-arve@android.com> (raw)
In-Reply-To: <1234316955-31304-6-git-send-email-arve@android.com>
If early-suspend support is enabled, early suspend handlers will be called
when a state other than "on" is written to /sys/power/request_state before
the unlocking "main" wakelock. Writing "on", locks the "main" wakelock and
calls the early-suspend resume handlers.
Signed-off-by: Arve Hjønnevåg <arve@android.com>
---
Documentation/power/early-suspend.txt | 26 +++++
include/linux/earlysuspend.h | 55 ++++++++++
kernel/power/Kconfig | 8 ++
kernel/power/Makefile | 1 +
kernel/power/earlysuspend.c | 174 +++++++++++++++++++++++++++++++++
kernel/power/power.h | 5 +
kernel/power/wakelock.c | 5 +
7 files changed, 274 insertions(+), 0 deletions(-)
create mode 100644 Documentation/power/early-suspend.txt
create mode 100755 include/linux/earlysuspend.h
create mode 100644 kernel/power/earlysuspend.c
diff --git a/Documentation/power/early-suspend.txt b/Documentation/power/early-suspend.txt
new file mode 100644
index 0000000..8286d3a
--- /dev/null
+++ b/Documentation/power/early-suspend.txt
@@ -0,0 +1,26 @@
+Early-suspend
+=============
+
+The early-suspend api allows drivers to get notified when user-space writes to
+/sys/power/request_state to indicate that the user visible sleep state should
+change. A level controls what order the handlers are called in. Suspend
+handlers are called in low to high level order, resume handlers are called in
+the opposite order.
+
+Four levels are defined:
+EARLY_SUSPEND_LEVEL_BLANK_SCREEN:
+ On suspend the screen should be turned off but the framebuffer must still be
+ accessible. On resume the screen can be turned back on.
+
+EARLY_SUSPEND_LEVEL_STOP_DRAWING:
+ On suspend this level notifies user-space that it should stop accessing the
+ framebuffer and it waits for it to complete. On resume it notifies user-space
+ that it should resume screen access.
+ Two methods are provided, console switch or a sysfs interface.
+
+EARLY_SUSPEND_LEVEL_DISABLE_FB:
+ Turn off the framebuffer on suspend and back on on resume.
+
+EARLY_SUSPEND_LEVEL_STOP_INPUT:
+ On suspend turn off input devices that are not capable of wakeup or where
+ wakeup is disabled. On resume turn the same devices back on.
diff --git a/include/linux/earlysuspend.h b/include/linux/earlysuspend.h
new file mode 100755
index 0000000..e3d03fb
--- /dev/null
+++ b/include/linux/earlysuspend.h
@@ -0,0 +1,55 @@
+/* include/linux/earlysuspend.h
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_EARLYSUSPEND_H
+#define _LINUX_EARLYSUSPEND_H
+
+#include <linux/list.h>
+
+/* The early_suspend structure defines suspend and resume hooks to be called
+ * when the user visible sleep state of the system changes, and a level to
+ * control the order. They can be used to turn off the screen and input
+ * devices that are not used for wakeup.
+ * Suspend handlers are called in low to high level order, resume handlers are
+ * called in the opposite order. If, when calling register_early_suspend,
+ * the suspend handlers have already been called without a matching call to the
+ * resume handlers, the suspend handler will be called directly from
+ * register_early_suspend. This direct call can violate the normal level order.
+ */
+enum {
+ EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,
+ EARLY_SUSPEND_LEVEL_STOP_INPUT = 75,
+ EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,
+ EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,
+};
+struct early_suspend {
+#ifdef CONFIG_EARLYSUSPEND
+ struct list_head link;
+ int level;
+ void (*suspend)(struct early_suspend *h);
+ void (*resume)(struct early_suspend *h);
+#endif
+};
+
+#ifdef CONFIG_EARLYSUSPEND
+void register_early_suspend(struct early_suspend *handler);
+void unregister_early_suspend(struct early_suspend *handler);
+#else
+#define register_early_suspend(handler) do { } while (0)
+#define unregister_early_suspend(handler) do { } while (0)
+#endif
+
+#endif
+
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index e784014..ccc55be 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -151,6 +151,14 @@ config USER_WAKELOCK
to create, lock and unlock a wakelock. The wakelock will be
deleted when the device is closed.
+config EARLYSUSPEND
+ bool "Early suspend"
+ depends on WAKELOCK
+ default y
+ ---help---
+ Call early suspend handlers when the user requested sleep state
+ changes.
+
config HIBERNATION
bool "Hibernation (aka 'suspend to disk')"
depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 63d30db..d3467b3 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_PM_SLEEP) += console.o
obj-$(CONFIG_FREEZER) += process.o
obj-$(CONFIG_WAKELOCK) += wakelock.o
obj-$(CONFIG_USER_WAKELOCK) += userwakelock.o
+obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o
obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
diff --git a/kernel/power/earlysuspend.c b/kernel/power/earlysuspend.c
new file mode 100644
index 0000000..4cdca39
--- /dev/null
+++ b/kernel/power/earlysuspend.c
@@ -0,0 +1,174 @@
+/* kernel/power/earlysuspend.c
+ *
+ * Copyright (C) 2005-2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/earlysuspend.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rtc.h>
+#include <linux/syscalls.h> /* sys_sync */
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+
+#include "power.h"
+
+enum {
+ DEBUG_SUSPEND = 1U << 0,
+ DEBUG_HANDLERS = 1U << 1,
+};
+static int debug_mask;
+module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+static DEFINE_MUTEX(early_suspend_lock);
+static LIST_HEAD(early_suspend_handlers);
+static void early_suspend(struct work_struct *work);
+static void late_resume(struct work_struct *work);
+static DECLARE_WORK(early_suspend_work, early_suspend);
+static DECLARE_WORK(late_resume_work, late_resume);
+static DEFINE_SPINLOCK(state_lock);
+enum {
+ SUSPEND_REQUESTED = 0x1,
+ SUSPENDED = 0x2,
+ SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED,
+};
+static int state;
+
+void register_early_suspend(struct early_suspend *handler)
+{
+ struct list_head *pos;
+
+ mutex_lock(&early_suspend_lock);
+ list_for_each(pos, &early_suspend_handlers) {
+ struct early_suspend *e;
+ e = list_entry(pos, struct early_suspend, link);
+ if (e->level > handler->level)
+ break;
+ }
+ list_add_tail(&handler->link, pos);
+ if ((state & SUSPENDED) && handler->suspend)
+ handler->suspend(handler);
+ mutex_unlock(&early_suspend_lock);
+}
+EXPORT_SYMBOL(register_early_suspend);
+
+void unregister_early_suspend(struct early_suspend *handler)
+{
+ mutex_lock(&early_suspend_lock);
+ list_del(&handler->link);
+ mutex_unlock(&early_suspend_lock);
+}
+EXPORT_SYMBOL(unregister_early_suspend);
+
+static void early_suspend(struct work_struct *work)
+{
+ struct early_suspend *pos;
+ unsigned long irqflags;
+ int abort = 0;
+
+ mutex_lock(&early_suspend_lock);
+ spin_lock_irqsave(&state_lock, irqflags);
+ if (state == SUSPEND_REQUESTED)
+ state |= SUSPENDED;
+ else
+ abort = 1;
+ spin_unlock_irqrestore(&state_lock, irqflags);
+
+ if (abort) {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("early_suspend: abort, state %d\n", state);
+ mutex_unlock(&early_suspend_lock);
+ goto abort;
+ }
+
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("early_suspend: call handlers\n");
+ list_for_each_entry(pos, &early_suspend_handlers, link) {
+ if (pos->suspend) {
+ if (debug_mask & DEBUG_HANDLERS)
+ pr_info("early_suspend: call %pF\n",
+ pos->suspend);
+ pos->suspend(pos);
+ if (debug_mask & DEBUG_HANDLERS)
+ pr_info("early_suspend: %pF returned\n",
+ pos->suspend);
+ }
+ }
+ mutex_unlock(&early_suspend_lock);
+
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("early_suspend: sync\n");
+
+ sys_sync();
+abort:
+ spin_lock_irqsave(&state_lock, irqflags);
+ if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
+ wake_unlock(&main_wake_lock);
+ spin_unlock_irqrestore(&state_lock, irqflags);
+}
+
+static void late_resume(struct work_struct *work)
+{
+ struct early_suspend *pos;
+ unsigned long irqflags;
+ int abort = 0;
+
+ mutex_lock(&early_suspend_lock);
+ spin_lock_irqsave(&state_lock, irqflags);
+ if (state == SUSPENDED)
+ state = 0; /* clear SUSPENDED */
+ else
+ abort = 1;
+ spin_unlock_irqrestore(&state_lock, irqflags);
+
+ if (abort) {
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("late_resume: abort, state %d\n", state);
+ goto abort;
+ }
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("late_resume: call handlers\n");
+ list_for_each_entry_reverse(pos, &early_suspend_handlers, link)
+ if (pos->resume) {
+ if (debug_mask & DEBUG_HANDLERS)
+ pr_info("early_suspend: call %pF\n",
+ pos->resume);
+ pos->resume(pos);
+ if (debug_mask & DEBUG_HANDLERS)
+ pr_info("early_suspend: %pF returned\n",
+ pos->resume);
+ }
+ if (debug_mask & DEBUG_SUSPEND)
+ pr_info("late_resume: done\n");
+abort:
+ mutex_unlock(&early_suspend_lock);
+}
+
+void request_early_suspend_state(bool on)
+{
+ unsigned long irqflags;
+ int old_sleep;
+
+ spin_lock_irqsave(&state_lock, irqflags);
+ old_sleep = state & SUSPEND_REQUESTED;
+ if (!old_sleep && !on) {
+ state |= SUSPEND_REQUESTED;
+ queue_work(suspend_work_queue, &early_suspend_work);
+ } else if (old_sleep && on) {
+ state &= ~SUSPEND_REQUESTED;
+ wake_lock(&main_wake_lock);
+ queue_work(suspend_work_queue, &late_resume_work);
+ }
+ spin_unlock_irqrestore(&state_lock, irqflags);
+}
+
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 9468679..7d8538d 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -230,3 +230,8 @@ extern struct workqueue_struct *suspend_work_queue;
extern struct wake_lock main_wake_lock;
void request_suspend_state(suspend_state_t state);
#endif
+
+#ifdef CONFIG_EARLYSUSPEND
+/* kernel/power/earlysuspend.c */
+void request_early_suspend_state(bool on);
+#endif
diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c
index c396b58..5048ad3 100644
--- a/kernel/power/wakelock.c
+++ b/kernel/power/wakelock.c
@@ -593,10 +593,15 @@ void request_suspend_state(suspend_state_t state)
tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
}
requested_suspend_state = state;
+
+#ifdef CONFIG_EARLYSUSPEND
+ request_early_suspend_state(state == PM_SUSPEND_ON);
+#else
if (state == PM_SUSPEND_ON)
wake_lock(&main_wake_lock);
else
wake_unlock(&main_wake_lock);
+#endif
spin_unlock_irqrestore(&state_lock, irqflags);
}
--
1.6.1
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/linux-pm
next prev parent reply other threads:[~2009-02-11 1:49 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-02-11 1:49 [RFC][PATCH 00/11] Android PM extensions (version 3) Arve Hjønnevåg
2009-02-11 1:49 ` [PATCH 01/10] PM: Add wake lock api Arve Hjønnevåg
2009-02-11 1:49 ` [PATCH 02/10] PM: wakelock: Override wakelocks when not using /sys/power/request_state Arve Hjønnevåg
2009-02-11 1:49 ` [PATCH 03/10] PM: wakelock: Add driver to access wakelocks from user-space Arve Hjønnevåg
2009-02-11 1:49 ` [PATCH 04/10] PM: wakelock: Abort task freezing if a wakelock is locked Arve Hjønnevåg
2009-02-11 1:49 ` [PATCH 05/10] PM: Add option to disable /sys/power/state interface Arve Hjønnevåg
2009-02-11 1:49 ` Arve Hjønnevåg [this message]
2009-02-11 1:49 ` [PATCH 07/10] PM: earlysuspend: Add console switch when user requested sleep state changes Arve Hjønnevåg
2009-02-11 1:49 ` [PATCH 08/10] PM: earlysuspend: Removing dependence on console Arve Hjønnevåg
2009-02-11 1:49 ` [PATCH 09/10] Input: Hold wake lock while event queue is not empty Arve Hjønnevåg
2009-02-11 1:49 ` [PATCH 10/10] ledtrig-sleep: Add led trigger for sleep debugging Arve Hjønnevåg
2009-02-12 11:31 ` [PATCH 09/10] Input: Hold wake lock while event queue is not empty Matthew Garrett
2009-02-13 0:27 ` Arve Hjønnevåg
2009-02-13 0:34 ` Matthew Garrett
2009-02-13 0:38 ` Arve Hjønnevåg
2009-02-13 0:40 ` Matthew Garrett
2009-02-13 0:52 ` Arve Hjønnevåg
2009-02-13 0:57 ` Matthew Garrett
2009-02-13 23:06 ` Rafael J. Wysocki
2009-02-13 23:51 ` Arve Hjønnevåg
2009-02-14 0:09 ` Matthew Garrett
2009-02-14 0:13 ` Arve Hjønnevåg
2009-02-14 0:18 ` Matthew Garrett
2009-02-12 11:28 ` [PATCH 07/10] PM: earlysuspend: Add console switch when user requested sleep state changes Matthew Garrett
2009-02-12 11:34 ` [PATCH 06/10] PM: Add early suspend api Matthew Garrett
2009-02-12 22:00 ` [PATCH 01/10] PM: Add wake lock api mark gross
2009-02-12 23:06 ` Arve Hjønnevåg
2009-02-17 21:05 ` [RFC][PATCH 00/11] Android PM extensions (version 3) Pavel Machek
2009-02-19 1:43 ` Arve Hjønnevåg
2009-02-19 12:54 ` Rafael J. Wysocki
2009-02-22 13:48 ` Pavel Machek
2009-02-23 23:31 ` Arve Hjønnevåg
2009-02-23 23:54 ` Rafael J. Wysocki
2009-02-25 13:23 ` Pavel Machek
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=1234316955-31304-7-git-send-email-arve@android.com \
--to=arve@android.com \
--cc=linux-pm@lists.linux-foundation.org \
--cc=ncunningham@crca.org.au \
--cc=swetland@google.com \
--cc=u.luckas@road.de \
/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.