| 1 | From ba801aa66e869f1f9f9d3a1610a2165baa024e4b Mon Sep 17 00:00:00 2001 |
| 2 | From: Rebecca Schultz <rschultz@google.com> |
| 3 | Date: Thu, 17 Jul 2008 18:14:55 -0700 |
| 4 | Subject: [PATCH 072/134] PM: earlysuspend: Removing dependence on console. |
| 5 | MIME-Version: 1.0 |
| 6 | Content-Type: text/plain; charset=utf-8 |
| 7 | Content-Transfer-Encoding: 8bit |
| 8 | |
| 9 | Rather than signaling a full update of the display from userspace via a |
| 10 | console switch, this patch introduces 2 files int /sys/power, |
| 11 | wait_for_fb_sleep and wait_for_fb_wake. Reading these files will block |
| 12 | until the requested state has been entered. When a read from |
| 13 | wait_for_fb_sleep returns userspace should stop drawing. When |
| 14 | wait_for_fb_wake returns, it should do a full update. If either are called |
| 15 | when the fb driver is already in the requested state, they will return |
| 16 | immediately. |
| 17 | |
| 18 | Signed-off-by: Rebecca Schultz <rschultz@google.com> |
| 19 | Signed-off-by: Arve Hjønnevåg <arve@android.com> |
| 20 | --- |
| 21 | kernel/power/Kconfig | 9 +++ |
| 22 | kernel/power/Makefile | 1 + |
| 23 | kernel/power/fbearlysuspend.c | 153 +++++++++++++++++++++++++++++++++++++++++ |
| 24 | 3 files changed, 163 insertions(+), 0 deletions(-) |
| 25 | create mode 100644 kernel/power/fbearlysuspend.c |
| 26 | |
| 27 | --- a/kernel/power/Kconfig |
| 28 | +++ b/kernel/power/Kconfig |
| 29 | @@ -159,6 +159,7 @@ config EARLYSUSPEND |
| 30 | |
| 31 | choice |
| 32 | prompt "User-space screen access" |
| 33 | + default FB_EARLYSUSPEND if !FRAMEBUFFER_CONSOLE |
| 34 | default CONSOLE_EARLYSUSPEND |
| 35 | depends on HAS_EARLYSUSPEND |
| 36 | |
| 37 | @@ -172,6 +173,14 @@ choice |
| 38 | Register early suspend handler to perform a console switch to |
| 39 | when user-space should stop drawing to the screen and a switch |
| 40 | back when it should resume. |
| 41 | + |
| 42 | + config FB_EARLYSUSPEND |
| 43 | + bool "Sysfs interface" |
| 44 | + depends on HAS_EARLYSUSPEND |
| 45 | + ---help--- |
| 46 | + Register early suspend handler that notifies and waits for |
| 47 | + user-space through sysfs when user-space should stop drawing |
| 48 | + to the screen and notifies user-space when it should resume. |
| 49 | endchoice |
| 50 | |
| 51 | config HIBERNATION |
| 52 | --- a/kernel/power/Makefile |
| 53 | +++ b/kernel/power/Makefile |
| 54 | @@ -10,6 +10,7 @@ obj-$(CONFIG_WAKELOCK) += wakelock.o |
| 55 | obj-$(CONFIG_USER_WAKELOCK) += userwakelock.o |
| 56 | obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o |
| 57 | obj-$(CONFIG_CONSOLE_EARLYSUSPEND) += consoleearlysuspend.o |
| 58 | +obj-$(CONFIG_FB_EARLYSUSPEND) += fbearlysuspend.o |
| 59 | obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o |
| 60 | |
| 61 | obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o |
| 62 | --- /dev/null |
| 63 | +++ b/kernel/power/fbearlysuspend.c |
| 64 | @@ -0,0 +1,153 @@ |
| 65 | +/* kernel/power/fbearlysuspend.c |
| 66 | + * |
| 67 | + * Copyright (C) 2005-2008 Google, Inc. |
| 68 | + * |
| 69 | + * This software is licensed under the terms of the GNU General Public |
| 70 | + * License version 2, as published by the Free Software Foundation, and |
| 71 | + * may be copied, distributed, and modified under those terms. |
| 72 | + * |
| 73 | + * This program is distributed in the hope that it will be useful, |
| 74 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 75 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 76 | + * GNU General Public License for more details. |
| 77 | + * |
| 78 | + */ |
| 79 | + |
| 80 | +#include <linux/earlysuspend.h> |
| 81 | +#include <linux/module.h> |
| 82 | +#include <linux/wait.h> |
| 83 | + |
| 84 | +#include "power.h" |
| 85 | + |
| 86 | +static wait_queue_head_t fb_state_wq; |
| 87 | +static DEFINE_SPINLOCK(fb_state_lock); |
| 88 | +static enum { |
| 89 | + FB_STATE_STOPPED_DRAWING, |
| 90 | + FB_STATE_REQUEST_STOP_DRAWING, |
| 91 | + FB_STATE_DRAWING_OK, |
| 92 | +} fb_state; |
| 93 | + |
| 94 | +/* tell userspace to stop drawing, wait for it to stop */ |
| 95 | +static void stop_drawing_early_suspend(struct early_suspend *h) |
| 96 | +{ |
| 97 | + int ret; |
| 98 | + unsigned long irq_flags; |
| 99 | + |
| 100 | + spin_lock_irqsave(&fb_state_lock, irq_flags); |
| 101 | + fb_state = FB_STATE_REQUEST_STOP_DRAWING; |
| 102 | + spin_unlock_irqrestore(&fb_state_lock, irq_flags); |
| 103 | + |
| 104 | + wake_up_all(&fb_state_wq); |
| 105 | + ret = wait_event_timeout(fb_state_wq, |
| 106 | + fb_state == FB_STATE_STOPPED_DRAWING, |
| 107 | + HZ); |
| 108 | + if (unlikely(fb_state != FB_STATE_STOPPED_DRAWING)) |
| 109 | + pr_warning("stop_drawing_early_suspend: timeout waiting for " |
| 110 | + "userspace to stop drawing\n"); |
| 111 | +} |
| 112 | + |
| 113 | +/* tell userspace to start drawing */ |
| 114 | +static void start_drawing_late_resume(struct early_suspend *h) |
| 115 | +{ |
| 116 | + unsigned long irq_flags; |
| 117 | + |
| 118 | + spin_lock_irqsave(&fb_state_lock, irq_flags); |
| 119 | + fb_state = FB_STATE_DRAWING_OK; |
| 120 | + spin_unlock_irqrestore(&fb_state_lock, irq_flags); |
| 121 | + wake_up(&fb_state_wq); |
| 122 | +} |
| 123 | + |
| 124 | +static struct early_suspend stop_drawing_early_suspend_desc = { |
| 125 | + .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING, |
| 126 | + .suspend = stop_drawing_early_suspend, |
| 127 | + .resume = start_drawing_late_resume, |
| 128 | +}; |
| 129 | + |
| 130 | +static ssize_t wait_for_fb_sleep_show(struct kobject *kobj, |
| 131 | + struct kobj_attribute *attr, char *buf) |
| 132 | +{ |
| 133 | + char *s = buf; |
| 134 | + int ret; |
| 135 | + |
| 136 | + ret = wait_event_interruptible(fb_state_wq, |
| 137 | + fb_state != FB_STATE_DRAWING_OK); |
| 138 | + if (ret && fb_state == FB_STATE_DRAWING_OK) |
| 139 | + return ret; |
| 140 | + else |
| 141 | + s += sprintf(buf, "sleeping"); |
| 142 | + return s - buf; |
| 143 | +} |
| 144 | + |
| 145 | +static ssize_t wait_for_fb_wake_show(struct kobject *kobj, |
| 146 | + struct kobj_attribute *attr, char *buf) |
| 147 | +{ |
| 148 | + char *s = buf; |
| 149 | + int ret; |
| 150 | + unsigned long irq_flags; |
| 151 | + |
| 152 | + spin_lock_irqsave(&fb_state_lock, irq_flags); |
| 153 | + if (fb_state == FB_STATE_REQUEST_STOP_DRAWING) { |
| 154 | + fb_state = FB_STATE_STOPPED_DRAWING; |
| 155 | + wake_up(&fb_state_wq); |
| 156 | + } |
| 157 | + spin_unlock_irqrestore(&fb_state_lock, irq_flags); |
| 158 | + |
| 159 | + ret = wait_event_interruptible(fb_state_wq, |
| 160 | + fb_state == FB_STATE_DRAWING_OK); |
| 161 | + if (ret && fb_state != FB_STATE_DRAWING_OK) |
| 162 | + return ret; |
| 163 | + else |
| 164 | + s += sprintf(buf, "awake"); |
| 165 | + |
| 166 | + return s - buf; |
| 167 | +} |
| 168 | + |
| 169 | +#define power_ro_attr(_name) \ |
| 170 | +static struct kobj_attribute _name##_attr = { \ |
| 171 | + .attr = { \ |
| 172 | + .name = __stringify(_name), \ |
| 173 | + .mode = 0444, \ |
| 174 | + }, \ |
| 175 | + .show = _name##_show, \ |
| 176 | + .store = NULL, \ |
| 177 | +} |
| 178 | + |
| 179 | +power_ro_attr(wait_for_fb_sleep); |
| 180 | +power_ro_attr(wait_for_fb_wake); |
| 181 | + |
| 182 | +static struct attribute *g[] = { |
| 183 | + &wait_for_fb_sleep_attr.attr, |
| 184 | + &wait_for_fb_wake_attr.attr, |
| 185 | + NULL, |
| 186 | +}; |
| 187 | + |
| 188 | +static struct attribute_group attr_group = { |
| 189 | + .attrs = g, |
| 190 | +}; |
| 191 | + |
| 192 | +static int __init android_power_init(void) |
| 193 | +{ |
| 194 | + int ret; |
| 195 | + |
| 196 | + init_waitqueue_head(&fb_state_wq); |
| 197 | + fb_state = FB_STATE_DRAWING_OK; |
| 198 | + |
| 199 | + ret = sysfs_create_group(power_kobj, &attr_group); |
| 200 | + if (ret) { |
| 201 | + pr_err("android_power_init: sysfs_create_group failed\n"); |
| 202 | + return ret; |
| 203 | + } |
| 204 | + |
| 205 | + register_early_suspend(&stop_drawing_early_suspend_desc); |
| 206 | + return 0; |
| 207 | +} |
| 208 | + |
| 209 | +static void __exit android_power_exit(void) |
| 210 | +{ |
| 211 | + unregister_early_suspend(&stop_drawing_early_suspend_desc); |
| 212 | + sysfs_remove_group(power_kobj, &attr_group); |
| 213 | +} |
| 214 | + |
| 215 | +module_init(android_power_init); |
| 216 | +module_exit(android_power_exit); |
| 217 | + |
| 218 | |