blob: 48773725988f1c0c45878de4e90fd70cb106d977 [file] [log] [blame]
Rebecca Schultz4b0ea272008-07-17 18:14:55 -07001/* kernel/power/fbearlysuspend.c
2 *
3 * Copyright (C) 2005-2008 Google, Inc.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16#include <linux/earlysuspend.h>
17#include <linux/module.h>
18#include <linux/wait.h>
19
20#include "power.h"
21
Amar Singhal2fb03542012-02-23 14:43:58 -080022#define MAX_BUF 100
23
Rebecca Schultz4b0ea272008-07-17 18:14:55 -070024static wait_queue_head_t fb_state_wq;
Amar Singhal2fb03542012-02-23 14:43:58 -080025static int display = 1;
Rebecca Schultz4b0ea272008-07-17 18:14:55 -070026static DEFINE_SPINLOCK(fb_state_lock);
27static enum {
28 FB_STATE_STOPPED_DRAWING,
29 FB_STATE_REQUEST_STOP_DRAWING,
30 FB_STATE_DRAWING_OK,
31} fb_state;
32
33/* tell userspace to stop drawing, wait for it to stop */
34static void stop_drawing_early_suspend(struct early_suspend *h)
35{
36 int ret;
37 unsigned long irq_flags;
38
39 spin_lock_irqsave(&fb_state_lock, irq_flags);
40 fb_state = FB_STATE_REQUEST_STOP_DRAWING;
41 spin_unlock_irqrestore(&fb_state_lock, irq_flags);
42
43 wake_up_all(&fb_state_wq);
44 ret = wait_event_timeout(fb_state_wq,
45 fb_state == FB_STATE_STOPPED_DRAWING,
46 HZ);
47 if (unlikely(fb_state != FB_STATE_STOPPED_DRAWING))
48 pr_warning("stop_drawing_early_suspend: timeout waiting for "
49 "userspace to stop drawing\n");
50}
51
52/* tell userspace to start drawing */
53static void start_drawing_late_resume(struct early_suspend *h)
54{
55 unsigned long irq_flags;
56
57 spin_lock_irqsave(&fb_state_lock, irq_flags);
58 fb_state = FB_STATE_DRAWING_OK;
59 spin_unlock_irqrestore(&fb_state_lock, irq_flags);
60 wake_up(&fb_state_wq);
61}
62
63static struct early_suspend stop_drawing_early_suspend_desc = {
64 .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING,
65 .suspend = stop_drawing_early_suspend,
66 .resume = start_drawing_late_resume,
67};
68
69static ssize_t wait_for_fb_sleep_show(struct kobject *kobj,
70 struct kobj_attribute *attr, char *buf)
71{
72 char *s = buf;
73 int ret;
74
75 ret = wait_event_interruptible(fb_state_wq,
76 fb_state != FB_STATE_DRAWING_OK);
Amar Singhal2fb03542012-02-23 14:43:58 -080077 if (ret && fb_state == FB_STATE_DRAWING_OK) {
Rebecca Schultz4b0ea272008-07-17 18:14:55 -070078 return ret;
Amar Singhal2fb03542012-02-23 14:43:58 -080079 } else {
Rebecca Schultz4b0ea272008-07-17 18:14:55 -070080 s += sprintf(buf, "sleeping");
Amar Singhal2fb03542012-02-23 14:43:58 -080081 if (display == 1) {
82 display = 0;
83 sysfs_notify(power_kobj, NULL, "wait_for_fb_status");
84 }
85 }
86
Rebecca Schultz4b0ea272008-07-17 18:14:55 -070087 return s - buf;
88}
89
90static ssize_t wait_for_fb_wake_show(struct kobject *kobj,
91 struct kobj_attribute *attr, char *buf)
92{
93 char *s = buf;
94 int ret;
95 unsigned long irq_flags;
96
97 spin_lock_irqsave(&fb_state_lock, irq_flags);
98 if (fb_state == FB_STATE_REQUEST_STOP_DRAWING) {
99 fb_state = FB_STATE_STOPPED_DRAWING;
100 wake_up(&fb_state_wq);
101 }
102 spin_unlock_irqrestore(&fb_state_lock, irq_flags);
103
104 ret = wait_event_interruptible(fb_state_wq,
105 fb_state == FB_STATE_DRAWING_OK);
106 if (ret && fb_state != FB_STATE_DRAWING_OK)
107 return ret;
Amar Singhal2fb03542012-02-23 14:43:58 -0800108 else {
Rebecca Schultz4b0ea272008-07-17 18:14:55 -0700109 s += sprintf(buf, "awake");
Amar Singhal2fb03542012-02-23 14:43:58 -0800110 if (display == 0) {
111 display = 1;
112 sysfs_notify(power_kobj, NULL, "wait_for_fb_status");
113 }
114 }
Rebecca Schultz4b0ea272008-07-17 18:14:55 -0700115 return s - buf;
116}
117
Amar Singhal2fb03542012-02-23 14:43:58 -0800118static ssize_t wait_for_fb_status_show(struct kobject *kobj,
119 struct kobj_attribute *attr, char *buf)
120{
121 int ret = 0;
122
123 if (display == 1)
124 ret = snprintf(buf, strnlen("on", MAX_BUF) + 1, "on");
125 else
126 ret = snprintf(buf, strnlen("off", MAX_BUF) + 1, "off");
127
128 return ret;
Rebecca Schultz4b0ea272008-07-17 18:14:55 -0700129}
130
Amar Singhal2fb03542012-02-23 14:43:58 -0800131#define power_ro_attr(_name) \
132 static struct kobj_attribute _name##_attr = { \
133 .attr = { \
134 .name = __stringify(_name), \
135 .mode = 0444, \
136 }, \
137 .show = _name##_show, \
138 .store = NULL, \
139 }
140
Rebecca Schultz4b0ea272008-07-17 18:14:55 -0700141power_ro_attr(wait_for_fb_sleep);
142power_ro_attr(wait_for_fb_wake);
Amar Singhal2fb03542012-02-23 14:43:58 -0800143power_ro_attr(wait_for_fb_status);
Rebecca Schultz4b0ea272008-07-17 18:14:55 -0700144
145static struct attribute *g[] = {
146 &wait_for_fb_sleep_attr.attr,
147 &wait_for_fb_wake_attr.attr,
Amar Singhal2fb03542012-02-23 14:43:58 -0800148 &wait_for_fb_status_attr.attr,
Rebecca Schultz4b0ea272008-07-17 18:14:55 -0700149 NULL,
150};
151
152static struct attribute_group attr_group = {
153 .attrs = g,
154};
155
156static int __init android_power_init(void)
157{
158 int ret;
159
160 init_waitqueue_head(&fb_state_wq);
161 fb_state = FB_STATE_DRAWING_OK;
162
163 ret = sysfs_create_group(power_kobj, &attr_group);
164 if (ret) {
165 pr_err("android_power_init: sysfs_create_group failed\n");
166 return ret;
167 }
168
169 register_early_suspend(&stop_drawing_early_suspend_desc);
170 return 0;
171}
172
173static void __exit android_power_exit(void)
174{
175 unregister_early_suspend(&stop_drawing_early_suspend_desc);
176 sysfs_remove_group(power_kobj, &attr_group);
177}
178
179module_init(android_power_init);
180module_exit(android_power_exit);
181