Merge tag 'v4.8-rc8' into drm-next
[cascardo/linux.git] / drivers / gpu / drm / drm_fb_helper.c
index 0a06f91..03414bd 100644 (file)
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/console.h>
 #include <linux/kernel.h>
 #include <linux/sysrq.h>
 #include <linux/slab.h>
-#include <linux/fb.h>
 #include <linux/module.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
@@ -41,6 +41,8 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 
+#include "drm_crtc_helper_internal.h"
+
 static bool drm_fbdev_emulation = true;
 module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
 MODULE_PARM_DESC(fbdev_emulation,
@@ -335,7 +337,7 @@ retry:
                        goto fail;
                }
 
-               plane_state->rotation = BIT(DRM_ROTATE_0);
+               plane_state->rotation = DRM_ROTATE_0;
 
                plane->old_fb = plane->fb;
                plane_mask |= 1 << drm_plane_index(plane);
@@ -395,7 +397,7 @@ static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
                if (dev->mode_config.rotation_property) {
                        drm_mode_plane_set_obj_prop(plane,
                                                    dev->mode_config.rotation_property,
-                                                   BIT(DRM_ROTATE_0));
+                                                   DRM_ROTATE_0);
                }
        }
 
@@ -618,6 +620,16 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
        kfree(helper->crtc_info);
 }
 
+static void drm_fb_helper_resume_worker(struct work_struct *work)
+{
+       struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
+                                                   resume_work);
+
+       console_lock();
+       fb_set_suspend(helper->fbdev, 0);
+       console_unlock();
+}
+
 static void drm_fb_helper_dirty_work(struct work_struct *work)
 {
        struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
@@ -649,6 +661,7 @@ void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
 {
        INIT_LIST_HEAD(&helper->kernel_fb_list);
        spin_lock_init(&helper->dirty_lock);
+       INIT_WORK(&helper->resume_work, drm_fb_helper_resume_worker);
        INIT_WORK(&helper->dirty_work, drm_fb_helper_dirty_work);
        helper->dirty_clip.x1 = helper->dirty_clip.y1 = ~0;
        helper->funcs = funcs;
@@ -1024,17 +1037,65 @@ EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit);
 /**
  * drm_fb_helper_set_suspend - wrapper around fb_set_suspend
  * @fb_helper: driver-allocated fbdev helper
- * @state: desired state, zero to resume, non-zero to suspend
+ * @suspend: whether to suspend or resume
  *
- * A wrapper around fb_set_suspend implemented by fbdev core
+ * A wrapper around fb_set_suspend implemented by fbdev core.
+ * Use drm_fb_helper_set_suspend_unlocked() if you don't need to take
+ * the lock yourself
  */
-void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, int state)
+void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend)
 {
        if (fb_helper && fb_helper->fbdev)
-               fb_set_suspend(fb_helper->fbdev, state);
+               fb_set_suspend(fb_helper->fbdev, suspend);
 }
 EXPORT_SYMBOL(drm_fb_helper_set_suspend);
 
+/**
+ * drm_fb_helper_set_suspend_unlocked - wrapper around fb_set_suspend that also
+ *                                      takes the console lock
+ * @fb_helper: driver-allocated fbdev helper
+ * @suspend: whether to suspend or resume
+ *
+ * A wrapper around fb_set_suspend() that takes the console lock. If the lock
+ * isn't available on resume, a worker is tasked with waiting for the lock
+ * to become available. The console lock can be pretty contented on resume
+ * due to all the printk activity.
+ *
+ * This function can be called multiple times with the same state since
+ * &fb_info->state is checked to see if fbdev is running or not before locking.
+ *
+ * Use drm_fb_helper_set_suspend() if you need to take the lock yourself.
+ */
+void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
+                                       bool suspend)
+{
+       if (!fb_helper || !fb_helper->fbdev)
+               return;
+
+       /* make sure there's no pending/ongoing resume */
+       flush_work(&fb_helper->resume_work);
+
+       if (suspend) {
+               if (fb_helper->fbdev->state != FBINFO_STATE_RUNNING)
+                       return;
+
+               console_lock();
+
+       } else {
+               if (fb_helper->fbdev->state == FBINFO_STATE_RUNNING)
+                       return;
+
+               if (!console_trylock()) {
+                       schedule_work(&fb_helper->resume_work);
+                       return;
+               }
+       }
+
+       fb_set_suspend(fb_helper->fbdev, suspend);
+       console_unlock();
+}
+EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked);
+
 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
                     u16 blue, u16 regno, struct fb_info *info)
 {
@@ -2194,7 +2255,7 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
  * @fb_helper: the drm_fb_helper
  *
  * Scan the connectors attached to the fb_helper and try to put together a
- * setup after *notification of a change in output configuration.
+ * setup after notification of a change in output configuration.
  *
  * Called at runtime, takes the mode config locks to be able to check/change the
  * modeset configuration. Must be run from process context (which usually means