|
@@ -26,6 +26,7 @@
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/kernel.h>
|
|
|
+#include <linux/console.h>
|
|
|
#include <linux/errno.h>
|
|
|
#include <linux/string.h>
|
|
|
#include <linux/mm.h>
|
|
@@ -636,6 +637,15 @@ out:
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+static void intel_fbdev_suspend_worker(struct work_struct *work)
|
|
|
+{
|
|
|
+ intel_fbdev_set_suspend(container_of(work,
|
|
|
+ struct drm_i915_private,
|
|
|
+ fbdev_suspend_work)->dev,
|
|
|
+ FBINFO_STATE_RUNNING,
|
|
|
+ true);
|
|
|
+}
|
|
|
+
|
|
|
int intel_fbdev_init(struct drm_device *dev)
|
|
|
{
|
|
|
struct intel_fbdev *ifbdev;
|
|
@@ -662,6 +672,8 @@ int intel_fbdev_init(struct drm_device *dev)
|
|
|
}
|
|
|
|
|
|
dev_priv->fbdev = ifbdev;
|
|
|
+ INIT_WORK(&dev_priv->fbdev_suspend_work, intel_fbdev_suspend_worker);
|
|
|
+
|
|
|
drm_fb_helper_single_add_all_connectors(&ifbdev->helper);
|
|
|
|
|
|
return 0;
|
|
@@ -682,12 +694,14 @@ void intel_fbdev_fini(struct drm_device *dev)
|
|
|
if (!dev_priv->fbdev)
|
|
|
return;
|
|
|
|
|
|
+ flush_work(&dev_priv->fbdev_suspend_work);
|
|
|
+
|
|
|
intel_fbdev_destroy(dev, dev_priv->fbdev);
|
|
|
kfree(dev_priv->fbdev);
|
|
|
dev_priv->fbdev = NULL;
|
|
|
}
|
|
|
|
|
|
-void intel_fbdev_set_suspend(struct drm_device *dev, int state)
|
|
|
+void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous)
|
|
|
{
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
struct intel_fbdev *ifbdev = dev_priv->fbdev;
|
|
@@ -698,6 +712,33 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state)
|
|
|
|
|
|
info = ifbdev->helper.fbdev;
|
|
|
|
|
|
+ if (synchronous) {
|
|
|
+ /* Flush any pending work to turn the console on, and then
|
|
|
+ * wait to turn it off. It must be synchronous as we are
|
|
|
+ * about to suspend or unload the driver.
|
|
|
+ *
|
|
|
+ * Note that from within the work-handler, we cannot flush
|
|
|
+ * ourselves, so only flush outstanding work upon suspend!
|
|
|
+ */
|
|
|
+ if (state != FBINFO_STATE_RUNNING)
|
|
|
+ flush_work(&dev_priv->fbdev_suspend_work);
|
|
|
+ console_lock();
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * The console lock can be pretty contented on resume due
|
|
|
+ * to all the printk activity. Try to keep it out of the hot
|
|
|
+ * path of resume if possible.
|
|
|
+ */
|
|
|
+ WARN_ON(state != FBINFO_STATE_RUNNING);
|
|
|
+ if (!console_trylock()) {
|
|
|
+ /* Don't block our own workqueue as this can
|
|
|
+ * be run in parallel with other i915.ko tasks.
|
|
|
+ */
|
|
|
+ schedule_work(&dev_priv->fbdev_suspend_work);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/* On resume from hibernation: If the object is shmemfs backed, it has
|
|
|
* been restored from swap. If the object is stolen however, it will be
|
|
|
* full of whatever garbage was left in there.
|
|
@@ -706,6 +747,7 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state)
|
|
|
memset_io(info->screen_base, 0, info->screen_size);
|
|
|
|
|
|
fb_set_suspend(info, state);
|
|
|
+ console_unlock();
|
|
|
}
|
|
|
|
|
|
void intel_fbdev_output_poll_changed(struct drm_device *dev)
|