1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
337cce26bSH Hartley Sweeten * Functions for saving/restoring console.
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Originally from swsusp.
61da177e4SLinus Torvalds */
71da177e4SLinus Torvalds
8f43f627dSJesse Barnes #include <linux/console.h>
91da177e4SLinus Torvalds #include <linux/vt_kern.h>
101da177e4SLinus Torvalds #include <linux/kbd_kern.h>
115ada918bSBernhard Walle #include <linux/vt.h>
12b6f448e9SAndres Salomon #include <linux/module.h>
131ff6bbfdSTejun Heo #include <linux/slab.h>
141da177e4SLinus Torvalds #include "power.h"
151da177e4SLinus Torvalds
1646cd2f32SRafael J. Wysocki #define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1)
1746cd2f32SRafael J. Wysocki
181da177e4SLinus Torvalds static int orig_fgconsole, orig_kmsg;
191da177e4SLinus Torvalds
20f43f627dSJesse Barnes static DEFINE_MUTEX(vt_switch_mutex);
21f43f627dSJesse Barnes
22f43f627dSJesse Barnes struct pm_vt_switch {
23f43f627dSJesse Barnes struct list_head head;
24f43f627dSJesse Barnes struct device *dev;
25f43f627dSJesse Barnes bool required;
26f43f627dSJesse Barnes };
27f43f627dSJesse Barnes
28f43f627dSJesse Barnes static LIST_HEAD(pm_vt_switch_list);
29f43f627dSJesse Barnes
30f43f627dSJesse Barnes
31f43f627dSJesse Barnes /**
32f43f627dSJesse Barnes * pm_vt_switch_required - indicate VT switch at suspend requirements
33f43f627dSJesse Barnes * @dev: device
34f43f627dSJesse Barnes * @required: if true, caller needs VT switch at suspend/resume time
35f43f627dSJesse Barnes *
36f43f627dSJesse Barnes * The different console drivers may or may not require VT switches across
37f43f627dSJesse Barnes * suspend/resume, depending on how they handle restoring video state and
38f43f627dSJesse Barnes * what may be running.
39f43f627dSJesse Barnes *
40f43f627dSJesse Barnes * Drivers can indicate support for switchless suspend/resume, which can
41f43f627dSJesse Barnes * save time and flicker, by using this routine and passing 'false' as
42f43f627dSJesse Barnes * the argument. If any loaded driver needs VT switching, or the
43f43f627dSJesse Barnes * no_console_suspend argument has been passed on the command line, VT
44f43f627dSJesse Barnes * switches will occur.
45f43f627dSJesse Barnes */
pm_vt_switch_required(struct device * dev,bool required)46f43f627dSJesse Barnes void pm_vt_switch_required(struct device *dev, bool required)
47f43f627dSJesse Barnes {
48f43f627dSJesse Barnes struct pm_vt_switch *entry, *tmp;
49f43f627dSJesse Barnes
50f43f627dSJesse Barnes mutex_lock(&vt_switch_mutex);
51f43f627dSJesse Barnes list_for_each_entry(tmp, &pm_vt_switch_list, head) {
52f43f627dSJesse Barnes if (tmp->dev == dev) {
53f43f627dSJesse Barnes /* already registered, update requirement */
54f43f627dSJesse Barnes tmp->required = required;
55f43f627dSJesse Barnes goto out;
56f43f627dSJesse Barnes }
57f43f627dSJesse Barnes }
58f43f627dSJesse Barnes
59f43f627dSJesse Barnes entry = kmalloc(sizeof(*entry), GFP_KERNEL);
60f43f627dSJesse Barnes if (!entry)
61f43f627dSJesse Barnes goto out;
62f43f627dSJesse Barnes
63f43f627dSJesse Barnes entry->required = required;
64f43f627dSJesse Barnes entry->dev = dev;
65f43f627dSJesse Barnes
66f43f627dSJesse Barnes list_add(&entry->head, &pm_vt_switch_list);
67f43f627dSJesse Barnes out:
68f43f627dSJesse Barnes mutex_unlock(&vt_switch_mutex);
69f43f627dSJesse Barnes }
70f43f627dSJesse Barnes EXPORT_SYMBOL(pm_vt_switch_required);
71f43f627dSJesse Barnes
72f43f627dSJesse Barnes /**
73f43f627dSJesse Barnes * pm_vt_switch_unregister - stop tracking a device's VT switching needs
74f43f627dSJesse Barnes * @dev: device
75f43f627dSJesse Barnes *
76f43f627dSJesse Barnes * Remove @dev from the vt switch list.
77f43f627dSJesse Barnes */
pm_vt_switch_unregister(struct device * dev)78f43f627dSJesse Barnes void pm_vt_switch_unregister(struct device *dev)
79f43f627dSJesse Barnes {
80f43f627dSJesse Barnes struct pm_vt_switch *tmp;
81f43f627dSJesse Barnes
82f43f627dSJesse Barnes mutex_lock(&vt_switch_mutex);
83f43f627dSJesse Barnes list_for_each_entry(tmp, &pm_vt_switch_list, head) {
84f43f627dSJesse Barnes if (tmp->dev == dev) {
85f43f627dSJesse Barnes list_del(&tmp->head);
86c6068504SMasami Ichikawa kfree(tmp);
87f43f627dSJesse Barnes break;
88f43f627dSJesse Barnes }
89f43f627dSJesse Barnes }
90f43f627dSJesse Barnes mutex_unlock(&vt_switch_mutex);
91f43f627dSJesse Barnes }
92f43f627dSJesse Barnes EXPORT_SYMBOL(pm_vt_switch_unregister);
93f43f627dSJesse Barnes
94f43f627dSJesse Barnes /*
95f43f627dSJesse Barnes * There are three cases when a VT switch on suspend/resume are required:
96f43f627dSJesse Barnes * 1) no driver has indicated a requirement one way or another, so preserve
97f43f627dSJesse Barnes * the old behavior
98f43f627dSJesse Barnes * 2) console suspend is disabled, we want to see debug messages across
99f43f627dSJesse Barnes * suspend/resume
100f43f627dSJesse Barnes * 3) any registered driver indicates it needs a VT switch
101f43f627dSJesse Barnes *
102f43f627dSJesse Barnes * If none of these conditions is present, meaning we have at least one driver
103f43f627dSJesse Barnes * that doesn't need the switch, and none that do, we can avoid it to make
104f43f627dSJesse Barnes * resume look a little prettier (and suspend too, but that's usually hidden,
105f43f627dSJesse Barnes * e.g. when closing the lid on a laptop).
106f43f627dSJesse Barnes */
pm_vt_switch(void)107f43f627dSJesse Barnes static bool pm_vt_switch(void)
108f43f627dSJesse Barnes {
109f43f627dSJesse Barnes struct pm_vt_switch *entry;
110f43f627dSJesse Barnes bool ret = true;
111f43f627dSJesse Barnes
112f43f627dSJesse Barnes mutex_lock(&vt_switch_mutex);
113f43f627dSJesse Barnes if (list_empty(&pm_vt_switch_list))
114f43f627dSJesse Barnes goto out;
115f43f627dSJesse Barnes
116f43f627dSJesse Barnes if (!console_suspend_enabled)
117f43f627dSJesse Barnes goto out;
118f43f627dSJesse Barnes
119f43f627dSJesse Barnes list_for_each_entry(entry, &pm_vt_switch_list, head) {
120f43f627dSJesse Barnes if (entry->required)
121f43f627dSJesse Barnes goto out;
122f43f627dSJesse Barnes }
123f43f627dSJesse Barnes
124f43f627dSJesse Barnes ret = false;
125f43f627dSJesse Barnes out:
126f43f627dSJesse Barnes mutex_unlock(&vt_switch_mutex);
127f43f627dSJesse Barnes return ret;
128f43f627dSJesse Barnes }
129f43f627dSJesse Barnes
pm_prepare_console(void)130ca5f2b4cSBorislav Petkov void pm_prepare_console(void)
1311da177e4SLinus Torvalds {
132f43f627dSJesse Barnes if (!pm_vt_switch())
133ca5f2b4cSBorislav Petkov return;
134f43f627dSJesse Barnes
1358d233558SAlan Cox orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1);
1368d233558SAlan Cox if (orig_fgconsole < 0)
137ca5f2b4cSBorislav Petkov return;
1381da177e4SLinus Torvalds
1395ada918bSBernhard Walle orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
140ca5f2b4cSBorislav Petkov return;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds
pm_restore_console(void)1431da177e4SLinus Torvalds void pm_restore_console(void)
1441da177e4SLinus Torvalds {
145f43f627dSJesse Barnes if (!pm_vt_switch())
146f43f627dSJesse Barnes return;
147f43f627dSJesse Barnes
1488d233558SAlan Cox if (orig_fgconsole >= 0) {
1498d233558SAlan Cox vt_move_to_console(orig_fgconsole, 0);
1505ada918bSBernhard Walle vt_kmsg_redirect(orig_kmsg);
1511da177e4SLinus Torvalds }
1528d233558SAlan Cox }
153