On Monday, February 04, 2013 01:37:20 PM Jesse Barnes wrote: > 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) > > Signed-off-by: Jesse Barnes <jbarnes at virtuousgeek.org> Acked-by: Rafael J. Wysocki <rafael.j.wysocki at intel.com> for all [1-3/3]. Thanks, Rafael > --- > include/linux/pm.h | 4 ++ > kernel/power/console.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 119 insertions(+) > > diff --git a/include/linux/pm.h b/include/linux/pm.h > index 03d7bb1..98310eb 100644 > --- a/include/linux/pm.h > +++ b/include/linux/pm.h > @@ -35,6 +35,10 @@ extern void (*pm_idle)(void); > extern void (*pm_power_off)(void); > extern void (*pm_power_off_prepare)(void); > > +struct device; /* we have a circular dep with device.h */ > +extern void pm_vt_switch_required(struct device *dev, bool required); > +extern void pm_vt_switch_unregister(struct device *dev); > + > /* > * Device power management > */ > diff --git a/kernel/power/console.c b/kernel/power/console.c > index b1dc456..4871ca9 100644 > --- a/kernel/power/console.c > +++ b/kernel/power/console.c > @@ -4,6 +4,7 @@ > * Originally from swsusp. > */ > > +#include <linux/console.h> > #include <linux/vt_kern.h> > #include <linux/kbd_kern.h> > #include <linux/vt.h> > @@ -14,8 +15,119 @@ > > static int orig_fgconsole, orig_kmsg; > > +DEFINE_MUTEX(vt_switch_mutex); > + > +struct pm_vt_switch { > + struct list_head head; > + struct device *dev; > + bool required; > +}; > + > +LIST_HEAD(pm_vt_switch_list); > + > + > +/** > + * pm_vt_switch_required - indicate VT switch at suspend requirements > + * @dev: device > + * @required: if true, caller needs VT switch at suspend/resume time > + * > + * The different console drivers may or may not require VT switches across > + * suspend/resume, depending on how they handle restoring video state and > + * what may be running. > + * > + * Drivers can indicate support for switchless suspend/resume, which can > + * save time and flicker, by using this routine and passing 'false' as > + * the argument. If any loaded driver needs VT switching, or the > + * no_console_suspend argument has been passed on the command line, VT > + * switches will occur. > + */ > +void pm_vt_switch_required(struct device *dev, bool required) > +{ > + struct pm_vt_switch *entry, *tmp; > + > + mutex_lock(&vt_switch_mutex); > + list_for_each_entry(tmp, &pm_vt_switch_list, head) { > + if (tmp->dev == dev) { > + /* already registered, update requirement */ > + tmp->required = required; > + goto out; > + } > + } > + > + entry = kmalloc(sizeof(*entry), GFP_KERNEL); > + if (!entry) > + goto out; > + > + entry->required = required; > + entry->dev = dev; > + > + list_add(&entry->head, &pm_vt_switch_list); > +out: > + mutex_unlock(&vt_switch_mutex); > +} > +EXPORT_SYMBOL(pm_vt_switch_required); > + > +/** > + * pm_vt_switch_unregister - stop tracking a device's VT switching needs > + * @dev: device > + * > + * Remove @dev from the vt switch list. > + */ > +void pm_vt_switch_unregister(struct device *dev) > +{ > + struct pm_vt_switch *tmp; > + > + mutex_lock(&vt_switch_mutex); > + list_for_each_entry(tmp, &pm_vt_switch_list, head) { > + if (tmp->dev == dev) { > + list_del(&tmp->head); > + break; > + } > + } > + mutex_unlock(&vt_switch_mutex); > +} > + > +/* > + * There are three cases when a VT switch on suspend/resume are required: > + * 1) no driver has indicated a requirement one way or another, so preserve > + * the old behavior > + * 2) console suspend is disabled, we want to see debug messages across > + * suspend/resume > + * 3) any registered driver indicates it needs a VT switch > + * > + * If none of these conditions is present, meaning we have at least one driver > + * that doesn't need the switch, and none that do, we can avoid it to make > + * resume look a little prettier (and suspend too, but that's usually hidden, > + * e.g. when closing the lid on a laptop). > + */ > +static bool pm_vt_switch(void) > +{ > + struct pm_vt_switch *entry; > + bool ret = true; > + > + mutex_lock(&vt_switch_mutex); > + if (list_empty(&pm_vt_switch_list)) > + goto out; > + > + if (!console_suspend_enabled) > + goto out; > + > + list_for_each_entry(entry, &pm_vt_switch_list, head) { > + if (entry->required) > + goto out; > + } > + > + ret = false; > +out: > + mutex_unlock(&vt_switch_mutex); > + return ret; > +} > + > int pm_prepare_console(void) > { > + if (!pm_vt_switch()) > + return 0; > + > orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1); > if (orig_fgconsole < 0) > return 1; > @@ -26,6 +138,9 @@ int pm_prepare_console(void) > > void pm_restore_console(void) > { > + if (!pm_vt_switch()) > + return; > + > if (orig_fgconsole >= 0) { > vt_move_to_console(orig_fgconsole, 0); > vt_kmsg_redirect(orig_kmsg); > -- I speak only for myself. Rafael J. Wysocki, Intel Open Source Technology Center.