diff options
author | Ke Qinghua <qinghua.ke@freescale.com> | 2014-04-04 16:22:34 +0800 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-21 22:35:45 -0500 |
commit | 24b7d5ca843cd63f1ee1ae7ebdb00f78dd3f62b9 (patch) | |
tree | a7c935a5401c93ad3bdec37a9957da95a5287406 | |
parent | 85c1fa95bf70927b33a0613edb690ae4759e0653 (diff) |
ENGR00305254-7 imx6sx sarbesd board bring up
Enable early suspend/late resume code
Signed-off-by: Ke Qinghua <qinghua.ke@freescale.com>
Acked-by: Jason Liu
-rw-r--r-- | drivers/input/misc/gpio_event.c | 39 | ||||
-rw-r--r-- | drivers/video/mxc/mxc_ipuv3_fb.c | 92 | ||||
-rw-r--r-- | include/linux/earlysuspend.h | 58 | ||||
-rw-r--r-- | kernel/power/Kconfig | 9 | ||||
-rw-r--r-- | kernel/power/Makefile | 3 | ||||
-rw-r--r-- | kernel/power/earlysuspend.c | 187 | ||||
-rw-r--r-- | kernel/power/fbearlysuspend.c | 152 | ||||
-rw-r--r-- | kernel/power/main.c | 122 | ||||
-rw-r--r-- | kernel/power/power.h | 12 |
9 files changed, 541 insertions, 133 deletions
diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c index 90f07eba3ce9..d4e5b4dfe19f 100644 --- a/drivers/input/misc/gpio_event.c +++ b/drivers/input/misc/gpio_event.c @@ -13,6 +13,7 @@ * */ +#include <linux/earlysuspend.h> #include <linux/module.h> #include <linux/input.h> #include <linux/gpio_event.h> @@ -23,6 +24,7 @@ struct gpio_event { struct gpio_event_input_devs *input_devs; const struct gpio_event_platform_data *info; + struct early_suspend early_suspend; void *state[0]; }; @@ -99,19 +101,23 @@ err_no_func: return ret; } -static void __maybe_unused gpio_event_suspend(struct gpio_event *ip) +#ifdef CONFIG_HAS_EARLYSUSPEND +void gpio_event_suspend(struct early_suspend *h) { + struct gpio_event *ip; + ip = container_of(h, struct gpio_event, early_suspend); gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND); - if (ip->info->power) - ip->info->power(ip->info, 0); + ip->info->power(ip->info, 0); } -static void __maybe_unused gpio_event_resume(struct gpio_event *ip) +void gpio_event_resume(struct early_suspend *h) { - if (ip->info->power) - ip->info->power(ip->info, 1); + struct gpio_event *ip; + ip = container_of(h, struct gpio_event, early_suspend); + ip->info->power(ip->info, 1); gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME); } +#endif static int gpio_event_probe(struct platform_device *pdev) { @@ -163,8 +169,15 @@ static int gpio_event_probe(struct platform_device *pdev) } ip->input_devs->count = dev_count; ip->info = event_info; - if (event_info->power) + if (event_info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + ip->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ip->early_suspend.suspend = gpio_event_suspend; + ip->early_suspend.resume = gpio_event_resume; + register_early_suspend(&ip->early_suspend); +#endif ip->info->power(ip->info, 1); + } err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT); if (err) @@ -185,8 +198,12 @@ static int gpio_event_probe(struct platform_device *pdev) err_input_register_device_failed: gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); err_call_all_func_failed: - if (event_info->power) + if (event_info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ip->early_suspend); +#endif ip->info->power(ip->info, 0); + } for (i = 0; i < registered; i++) input_unregister_device(ip->input_devs->dev[i]); for (i = dev_count - 1; i >= registered; i--) { @@ -205,8 +222,12 @@ static int gpio_event_remove(struct platform_device *pdev) int i; gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); - if (ip->info->power) + if (ip->info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ip->early_suspend); +#endif ip->info->power(ip->info, 0); + } for (i = 0; i < ip->input_devs->count; i++) input_unregister_device(ip->input_devs->dev[i]); kfree(ip); diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index 57dad6f90725..bc853225d6c3 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -49,6 +49,7 @@ #include <linux/string.h> #include <linux/uaccess.h> +#include <linux/earlysuspend.h> #include "mxc_dispdrv.h" /* @@ -99,6 +100,9 @@ struct mxcfb_info { struct fb_var_screeninfo cur_var; ktime_t vsync_nf_timestamp; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend fbdrv_earlysuspend; +#endif }; struct mxcfb_pfmt { @@ -136,6 +140,10 @@ enum { static bool g_dp_in_use[2]; LIST_HEAD(fb_alloc_list); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mxcfb_early_suspend(struct early_suspend *h); +static void mxcfb_later_resume(struct early_suspend *h); +#endif /* Return default standard(RGB) pixel format */ static uint32_t bpp_to_pixfmt(int bpp) @@ -1685,7 +1693,7 @@ static irqreturn_t mxcfb_alpha_irq_handler(int irq, void *dev_id) /* * Suspends the framebuffer and blanks the screen. Power management support */ -static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state) +static int mxcfb_core_suspend(struct platform_device *pdev, pm_message_t state) { struct fb_info *fbi = platform_get_drvdata(pdev); struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; @@ -1717,9 +1725,22 @@ static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state) } /* + * Suspends the framebuffer and blanks the screen. Power management support + */ +static int mxcfb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct fb_info *fbi = platform_get_drvdata(pdev); + struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + + if (strstr(mxc_fbi->dispdrv->drv->name, "hdmi")) + return mxcfb_core_suspend(pdev, state); + + return 0; +} +/* * Resumes the framebuffer and unblanks the screen. Power management support */ -static int mxcfb_resume(struct platform_device *pdev) +static int mxcfb_core_resume(struct platform_device *pdev) { struct fb_info *fbi = platform_get_drvdata(pdev); struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; @@ -1737,6 +1758,18 @@ static int mxcfb_resume(struct platform_device *pdev) fb_set_suspend(mxc_fbi->ovfbi, 0); console_unlock(); } + return 0; +} +/* + * Resumes the framebuffer and unblanks the screen. Power management support + */ +static int mxcfb_resume(struct platform_device *pdev) +{ + struct fb_info *fbi = platform_get_drvdata(pdev); + struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + + if (strstr(mxc_fbi->dispdrv->drv->name, "hdmi")) + return mxcfb_core_resume(pdev); return 0; } @@ -2515,6 +2548,13 @@ static int mxcfb_probe(struct platform_device *pdev) "Error %d on creating file\n", ret); } +#ifdef CONFIG_HAS_EARLYSUSPEND + mxcfbi->fbdrv_earlysuspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + mxcfbi->fbdrv_earlysuspend.suspend = mxcfb_early_suspend; + mxcfbi->fbdrv_earlysuspend.resume = mxcfb_later_resume; + mxcfbi->fbdrv_earlysuspend.data = pdev; + register_early_suspend(&mxcfbi->fbdrv_earlysuspend); +#endif return 0; mxcfb_setupoverlay_failed: @@ -2537,6 +2577,9 @@ static int mxcfb_remove(struct platform_device *pdev) if (!fbi) return 0; +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&mxc_fbi->fbdrv_earlysuspend); +#endif device_remove_file(fbi->dev, &dev_attr_fsl_disp_dev_property); device_remove_file(fbi->dev, &dev_attr_fsl_disp_property); @@ -2579,6 +2622,51 @@ static struct platform_driver mxcfb_driver = { .suspend = mxcfb_suspend, .resume = mxcfb_resume, }; +#ifdef CONFIG_HAS_EARLYSUSPEND +static void mxcfb_early_suspend(struct early_suspend *h) +{ + struct platform_device *pdev = (struct platform_device *)h->data; + struct fb_info *fbi = platform_get_drvdata(pdev); + struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par; + pm_message_t state = { .event = PM_EVENT_SUSPEND }; + struct fb_event event; + int blank = FB_BLANK_POWERDOWN; + + if (mxcfbi->ipu_ch == MEM_FG_SYNC) + return; + + if (strstr(mxcfbi->dispdrv->drv->name, "hdmi")) { + /* Only black the hdmi fb due to audio dependency */ + memset(fbi->screen_base, 0, fbi->fix.smem_len); + return; + } + + mxcfb_core_suspend(pdev, state); + event.info = fbi; + event.data = ␣ + fb_notifier_call_chain(FB_EVENT_BLANK, &event); +} + +static void mxcfb_later_resume(struct early_suspend *h) +{ + struct platform_device *pdev = (struct platform_device *)h->data; + struct fb_info *fbi = platform_get_drvdata(pdev); + struct mxcfb_info *mxcfbi = (struct mxcfb_info *)fbi->par; + struct fb_event event; + + if (mxcfbi->ipu_ch == MEM_FG_SYNC) + return; + + /* HDMI resume function has been called */ + if (strstr(mxcfbi->dispdrv->drv->name, "hdmi")) + return; + + mxcfb_core_resume(pdev); + event.info = fbi; + event.data = &mxcfbi->next_blank; + fb_notifier_call_chain(FB_EVENT_BLANK, &event); +} +#endif /*! * Main entry function for the framebuffer. The function registers the power diff --git a/include/linux/earlysuspend.h b/include/linux/earlysuspend.h new file mode 100644 index 000000000000..cddd0b470179 --- /dev/null +++ b/include/linux/earlysuspend.h @@ -0,0 +1,58 @@ +/* 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 + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include <linux/list.h> +#endif + +/* 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_DRAWING = 100, + EARLY_SUSPEND_LEVEL_DISABLE_FB = 150, + EARLY_SUSPEND_LEVEL_POST_DISABLE_FB = 200, +}; +struct early_suspend { +#ifdef CONFIG_HAS_EARLYSUSPEND + struct list_head link; + int level; + void *data; + void (*suspend)(struct early_suspend *h); + void (*resume)(struct early_suspend *h); +#endif +}; + +#ifdef CONFIG_HAS_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 f8cc6c43efec..cbedafb24ea0 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -21,6 +21,15 @@ config SUSPEND_FREEZER config HAS_WAKELOCK bool default y +config HAS_EARLYSUSPEND + bool + default y +config EARLYSUSPEND + bool + default y +config FB_EARLYSUSPEND + bool + default y config WAKELOCK bool diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 74c713ba61b0..fd354a5e8a24 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -11,6 +11,9 @@ obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ block_io.o obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o +obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o +obj-$(CONFIG_FB_EARLYSUSPEND) += fbearlysuspend.o +obj-$(CONFIG_CPUFREQ_GOV_ON_EARLYSUPSEND) += cpufreq_earlysuspend.o obj-$(CONFIG_SUSPEND_TIME) += suspend_time.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o diff --git a/kernel/power/earlysuspend.c b/kernel/power/earlysuspend.c new file mode 100644 index 000000000000..5971c78cf606 --- /dev/null +++ b/kernel/power/earlysuspend.c @@ -0,0 +1,187 @@ +/* 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_USER_STATE = 1U << 0, + DEBUG_SUSPEND = 1U << 2, + DEBUG_VERBOSE = 1U << 3, +}; +static int debug_mask = DEBUG_USER_STATE; +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 != NULL) { + if (debug_mask & DEBUG_VERBOSE) + pr_info("early_suspend: calling %pf\n", pos->suspend); + pos->suspend(pos); + } + } + 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) + android_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 &= ~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 != NULL) { + if (debug_mask & DEBUG_VERBOSE) + pr_info("late_resume: calling %pf\n", pos->resume); + + pos->resume(pos); + } + } + if (debug_mask & DEBUG_SUSPEND) + pr_info("late_resume: done\n"); +abort: + mutex_unlock(&early_suspend_lock); +} + +void request_suspend_state(suspend_state_t new_state) +{ + unsigned long irqflags; + int old_sleep; + + spin_lock_irqsave(&state_lock, irqflags); + old_sleep = state & SUSPEND_REQUESTED; + if (debug_mask & DEBUG_USER_STATE) { + struct timespec ts; + struct rtc_time tm; + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + pr_info("request_suspend_state: %s (%d->%d) at %lld " + "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", + new_state != PM_SUSPEND_ON ? "sleep" : "wakeup", + requested_suspend_state, new_state, + ktime_to_ns(ktime_get()), + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); + } + if (!old_sleep && new_state != PM_SUSPEND_ON) { + state |= SUSPEND_REQUESTED; + queue_work(suspend_work_queue, &early_suspend_work); + } else if (old_sleep && new_state == PM_SUSPEND_ON) { + state &= ~SUSPEND_REQUESTED; + android_wake_lock(&main_wake_lock); + queue_work(suspend_work_queue, &late_resume_work); + } + requested_suspend_state = new_state; + spin_unlock_irqrestore(&state_lock, irqflags); +} + +suspend_state_t get_suspend_state(void) +{ + return requested_suspend_state; +} diff --git a/kernel/power/fbearlysuspend.c b/kernel/power/fbearlysuspend.c new file mode 100644 index 000000000000..0142c9d30ce5 --- /dev/null +++ b/kernel/power/fbearlysuspend.c @@ -0,0 +1,152 @@ +/* kernel/power/fbearlysuspend.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/wait.h> + +#include "power.h" + +static wait_queue_head_t fb_state_wq; +static DEFINE_SPINLOCK(fb_state_lock); +static enum { + FB_STATE_STOPPED_DRAWING, + FB_STATE_REQUEST_STOP_DRAWING, + FB_STATE_DRAWING_OK, +} fb_state; + +/* tell userspace to stop drawing, wait for it to stop */ +static void stop_drawing_early_suspend(struct early_suspend *h) +{ + int ret; + unsigned long irq_flags; + + spin_lock_irqsave(&fb_state_lock, irq_flags); + fb_state = FB_STATE_REQUEST_STOP_DRAWING; + spin_unlock_irqrestore(&fb_state_lock, irq_flags); + + wake_up_all(&fb_state_wq); + ret = wait_event_timeout(fb_state_wq, + fb_state == FB_STATE_STOPPED_DRAWING, + HZ); + if (unlikely(fb_state != FB_STATE_STOPPED_DRAWING)) + pr_warning("stop_drawing_early_suspend: timeout waiting for " + "userspace to stop drawing\n"); +} + +/* tell userspace to start drawing */ +static void start_drawing_late_resume(struct early_suspend *h) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&fb_state_lock, irq_flags); + fb_state = FB_STATE_DRAWING_OK; + spin_unlock_irqrestore(&fb_state_lock, irq_flags); + wake_up(&fb_state_wq); +} + +static struct early_suspend stop_drawing_early_suspend_desc = { + .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING, + .suspend = stop_drawing_early_suspend, + .resume = start_drawing_late_resume, +}; + +static ssize_t wait_for_fb_sleep_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *s = buf; + int ret; + + ret = wait_event_interruptible(fb_state_wq, + fb_state != FB_STATE_DRAWING_OK); + if (ret && fb_state == FB_STATE_DRAWING_OK) + return ret; + else + s += sprintf(buf, "sleeping"); + return s - buf; +} + +static ssize_t wait_for_fb_wake_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + char *s = buf; + int ret; + unsigned long irq_flags; + + spin_lock_irqsave(&fb_state_lock, irq_flags); + if (fb_state == FB_STATE_REQUEST_STOP_DRAWING) { + fb_state = FB_STATE_STOPPED_DRAWING; + wake_up(&fb_state_wq); + } + spin_unlock_irqrestore(&fb_state_lock, irq_flags); + + ret = wait_event_interruptible(fb_state_wq, + fb_state == FB_STATE_DRAWING_OK); + if (ret && fb_state != FB_STATE_DRAWING_OK) + return ret; + else + s += sprintf(buf, "awake"); + + return s - buf; +} + +#define power_ro_attr(_name) \ +static struct kobj_attribute _name##_attr = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = 0444, \ + }, \ + .show = _name##_show, \ + .store = NULL, \ +} + +power_ro_attr(wait_for_fb_sleep); +power_ro_attr(wait_for_fb_wake); + +static struct attribute *g[] = { + &wait_for_fb_sleep_attr.attr, + &wait_for_fb_wake_attr.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = g, +}; + +static int __init android_power_init(void) +{ + int ret; + + init_waitqueue_head(&fb_state_wq); + fb_state = FB_STATE_DRAWING_OK; + + ret = sysfs_create_group(power_kobj, &attr_group); + if (ret) { + pr_err("android_power_init: sysfs_create_group failed\n"); + return ret; + } + register_early_suspend(&stop_drawing_early_suspend_desc); + return 0; +} + +static void __exit android_power_exit(void) +{ + unregister_early_suspend(&stop_drawing_early_suspend_desc); + sysfs_remove_group(power_kobj, &attr_group); +} + +module_init(android_power_init); +module_exit(android_power_exit); + diff --git a/kernel/power/main.c b/kernel/power/main.c index 1d4da28f9067..63fbe7347b3e 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -338,128 +338,6 @@ static suspend_state_t decode_state(const char *buf, size_t n) return PM_SUSPEND_ON; } -#include <linux/wakelock.h> -#include <linux/module.h> - -enum { - DEBUG_USER_STATE = 1U << 0, - DEBUG_SUSPEND = 1U << 2, - DEBUG_VERBOSE = 1U << 3, -}; -static int debug_mask = DEBUG_USER_STATE; -module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); - -extern struct workqueue_struct *suspend_work_queue; -extern struct wake_lock main_wake_lock; -extern suspend_state_t requested_suspend_state; - -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; - -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"); - /* early suspend not enable */ - 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) - android_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 &= ~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; - } - /* early suspend not enable */ -abort: - mutex_unlock(&early_suspend_lock); -} - -static void request_suspend_state(suspend_state_t new_state) -{ - unsigned long irqflags; - int old_sleep; - - spin_lock_irqsave(&state_lock, irqflags); - old_sleep = state & SUSPEND_REQUESTED; - if (debug_mask & DEBUG_USER_STATE) { - struct timespec ts; - struct rtc_time tm; - getnstimeofday(&ts); - rtc_time_to_tm(ts.tv_sec, &tm); - pr_info("request_suspend_state: %s (%d->%d) at %lld " - "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", - new_state != PM_SUSPEND_ON ? "sleep" : "wakeup", - requested_suspend_state, new_state, - ktime_to_ns(ktime_get()), - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); - } - if (!old_sleep && new_state != PM_SUSPEND_ON) { - state |= SUSPEND_REQUESTED; - queue_work(suspend_work_queue, &early_suspend_work); - } else if (old_sleep && new_state == PM_SUSPEND_ON) { - state &= ~SUSPEND_REQUESTED; - android_wake_lock(&main_wake_lock); - queue_work(suspend_work_queue, &late_resume_work); - } - requested_suspend_state = new_state; - spin_unlock_irqrestore(&state_lock, irqflags); -} - static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { diff --git a/kernel/power/power.h b/kernel/power/power.h index 7d4b7ffb3c1d..e101cac0ef2d 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -294,3 +294,15 @@ extern int pm_wake_lock(const char *buf); extern int pm_wake_unlock(const char *buf); #endif /* !CONFIG_PM_WAKELOCKS */ +#ifdef CONFIG_WAKELOCK +/* kernel/power/wakelock.c */ +extern struct workqueue_struct *suspend_work_queue; +extern struct wake_lock main_wake_lock; +extern suspend_state_t requested_suspend_state; +#endif + +#ifdef CONFIG_EARLYSUSPEND +/* kernel/power/earlysuspend.c */ +void request_suspend_state(suspend_state_t state); +suspend_state_t get_suspend_state(void); +#endif |