aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@virtuousgeek.org>2013-02-04 08:37:20 -0500
committerDaniel Vetter <daniel.vetter@ffwll.ch>2013-02-19 19:33:41 -0500
commitf43f627d2f17e95c78647eeddf968d12f5c286b1 (patch)
tree6422cc20ed378f976a7ef4408921c712e5299dc8
parent210561ffd72d00eccf12c0131b8024d5436bae95 (diff)
PM: make VT switching to the suspend console optional v3
KMS drivers can potentially restore the display configuration without userspace help. Such drivers can can call a new funciton, pm_vt_switch_required(false) if they support this feature. In that case, the PM layer won't VT switch to the suspend console at suspend time and then back to the original VT on resume, but rather leave things alone for a nicer looking suspend and resume sequence. v2: make a function so we can handle multiple drivers (Alan) v3: use a list to track device requests (Rafael) v4: Squash in build fix from Jesse for CONFIG_VT_CONSOLE_SLEEP=n v5: Squash in patch from Wu Fengguang to add a few missing static qualifiers. v6: Add missing EXPORT_SYMBOL. Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Reviewed-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> (v3) Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
-rw-r--r--include/linux/pm.h13
-rw-r--r--kernel/power/console.c116
2 files changed, 129 insertions, 0 deletions
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 03d7bb145311..e5da2f353e8f 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -35,6 +35,19 @@ extern void (*pm_idle)(void);
35extern void (*pm_power_off)(void); 35extern void (*pm_power_off)(void);
36extern void (*pm_power_off_prepare)(void); 36extern void (*pm_power_off_prepare)(void);
37 37
38struct device; /* we have a circular dep with device.h */
39#ifdef CONFIG_VT_CONSOLE_SLEEP
40extern void pm_vt_switch_required(struct device *dev, bool required);
41extern void pm_vt_switch_unregister(struct device *dev);
42#else
43static inline void pm_vt_switch_required(struct device *dev, bool required)
44{
45}
46static inline void pm_vt_switch_unregister(struct device *dev)
47{
48}
49#endif /* CONFIG_VT_CONSOLE_SLEEP */
50
38/* 51/*
39 * Device power management 52 * Device power management
40 */ 53 */
diff --git a/kernel/power/console.c b/kernel/power/console.c
index b1dc456474b5..463aa6736751 100644
--- a/kernel/power/console.c
+++ b/kernel/power/console.c
@@ -4,6 +4,7 @@
4 * Originally from swsusp. 4 * Originally from swsusp.
5 */ 5 */
6 6
7#include <linux/console.h>
7#include <linux/vt_kern.h> 8#include <linux/vt_kern.h>
8#include <linux/kbd_kern.h> 9#include <linux/kbd_kern.h>
9#include <linux/vt.h> 10#include <linux/vt.h>
@@ -14,8 +15,120 @@
14 15
15static int orig_fgconsole, orig_kmsg; 16static int orig_fgconsole, orig_kmsg;
16 17
18static DEFINE_MUTEX(vt_switch_mutex);
19
20struct pm_vt_switch {
21 struct list_head head;
22 struct device *dev;
23 bool required;
24};
25
26static LIST_HEAD(pm_vt_switch_list);
27
28
29/**
30 * pm_vt_switch_required - indicate VT switch at suspend requirements
31 * @dev: device
32 * @required: if true, caller needs VT switch at suspend/resume time
33 *
34 * The different console drivers may or may not require VT switches across
35 * suspend/resume, depending on how they handle restoring video state and
36 * what may be running.
37 *
38 * Drivers can indicate support for switchless suspend/resume, which can
39 * save time and flicker, by using this routine and passing 'false' as
40 * the argument. If any loaded driver needs VT switching, or the
41 * no_console_suspend argument has been passed on the command line, VT
42 * switches will occur.
43 */
44void pm_vt_switch_required(struct device *dev, bool required)
45{
46 struct pm_vt_switch *entry, *tmp;
47
48 mutex_lock(&vt_switch_mutex);
49 list_for_each_entry(tmp, &pm_vt_switch_list, head) {
50 if (tmp->dev == dev) {
51 /* already registered, update requirement */
52 tmp->required = required;
53 goto out;
54 }
55 }
56
57 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
58 if (!entry)
59 goto out;
60
61 entry->required = required;
62 entry->dev = dev;
63
64 list_add(&entry->head, &pm_vt_switch_list);
65out:
66 mutex_unlock(&vt_switch_mutex);
67}
68EXPORT_SYMBOL(pm_vt_switch_required);
69
70/**
71 * pm_vt_switch_unregister - stop tracking a device's VT switching needs
72 * @dev: device
73 *
74 * Remove @dev from the vt switch list.
75 */
76void pm_vt_switch_unregister(struct device *dev)
77{
78 struct pm_vt_switch *tmp;
79
80 mutex_lock(&vt_switch_mutex);
81 list_for_each_entry(tmp, &pm_vt_switch_list, head) {
82 if (tmp->dev == dev) {
83 list_del(&tmp->head);
84 break;
85 }
86 }
87 mutex_unlock(&vt_switch_mutex);
88}
89EXPORT_SYMBOL(pm_vt_switch_unregister);
90
91/*
92 * There are three cases when a VT switch on suspend/resume are required:
93 * 1) no driver has indicated a requirement one way or another, so preserve
94 * the old behavior
95 * 2) console suspend is disabled, we want to see debug messages across
96 * suspend/resume
97 * 3) any registered driver indicates it needs a VT switch
98 *
99 * If none of these conditions is present, meaning we have at least one driver
100 * that doesn't need the switch, and none that do, we can avoid it to make
101 * resume look a little prettier (and suspend too, but that's usually hidden,
102 * e.g. when closing the lid on a laptop).
103 */
104static bool pm_vt_switch(void)
105{
106 struct pm_vt_switch *entry;
107 bool ret = true;
108
109 mutex_lock(&vt_switch_mutex);
110 if (list_empty(&pm_vt_switch_list))
111 goto out;
112
113 if (!console_suspend_enabled)
114 goto out;
115
116 list_for_each_entry(entry, &pm_vt_switch_list, head) {
117 if (entry->required)
118 goto out;
119 }
120
121 ret = false;
122out:
123 mutex_unlock(&vt_switch_mutex);
124 return ret;
125}
126
17int pm_prepare_console(void) 127int pm_prepare_console(void)
18{ 128{
129 if (!pm_vt_switch())
130 return 0;
131
19 orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1); 132 orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1);
20 if (orig_fgconsole < 0) 133 if (orig_fgconsole < 0)
21 return 1; 134 return 1;
@@ -26,6 +139,9 @@ int pm_prepare_console(void)
26 139
27void pm_restore_console(void) 140void pm_restore_console(void)
28{ 141{
142 if (!pm_vt_switch())
143 return;
144
29 if (orig_fgconsole >= 0) { 145 if (orig_fgconsole >= 0) {
30 vt_move_to_console(orig_fgconsole, 0); 146 vt_move_to_console(orig_fgconsole, 0);
31 vt_kmsg_redirect(orig_kmsg); 147 vt_kmsg_redirect(orig_kmsg);