These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / gpu / drm / drm_fb_helper.c
index cac4229..69cbab5 100644 (file)
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+
+static bool drm_fbdev_emulation = true;
+module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
+MODULE_PARM_DESC(fbdev_emulation,
+                "Enable legacy fbdev emulation [default=true]");
 
 static LIST_HEAD(kernel_fb_helper_list);
 
@@ -56,8 +63,8 @@ static LIST_HEAD(kernel_fb_helper_list);
  * Teardown is done with drm_fb_helper_fini().
  *
  * At runtime drivers should restore the fbdev console by calling
- * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
- * should also notify the fb helper code from updates to the output
+ * drm_fb_helper_restore_fbdev_mode_unlocked() from their ->lastclose callback.
+ * They should also notify the fb helper code from updates to the output
  * configuration by calling drm_fb_helper_hotplug_event(). For easier
  * integration with the output polling code in drm_crtc_helper.c the modeset
  * code provides a ->output_poll_changed callback.
@@ -89,8 +96,9 @@ static LIST_HEAD(kernel_fb_helper_list);
  * connectors to the fbdev, e.g. if some are reserved for special purposes or
  * not adequate to be used for the fbcon.
  *
- * Since this is part of the initial setup before the fbdev is published, no
- * locking is required.
+ * This function is protected against concurrent connector hotadds/removals
+ * using drm_fb_helper_add_one_connector() and
+ * drm_fb_helper_remove_one_connector().
  */
 int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
 {
@@ -98,7 +106,11 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
        struct drm_connector *connector;
        int i;
 
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+       if (!drm_fbdev_emulation)
+               return 0;
+
+       mutex_lock(&dev->mode_config.mutex);
+       drm_for_each_connector(connector, dev) {
                struct drm_fb_helper_connector *fb_helper_connector;
 
                fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
@@ -108,6 +120,7 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
                fb_helper_connector->connector = connector;
                fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
        }
+       mutex_unlock(&dev->mode_config.mutex);
        return 0;
 fail:
        for (i = 0; i < fb_helper->connector_count; i++) {
@@ -115,6 +128,8 @@ fail:
                fb_helper->connector_info[i] = NULL;
        }
        fb_helper->connector_count = 0;
+       mutex_unlock(&dev->mode_config.mutex);
+
        return -ENOMEM;
 }
 EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
@@ -124,6 +139,9 @@ int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_
        struct drm_fb_helper_connector **temp;
        struct drm_fb_helper_connector *fb_helper_connector;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
        if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) {
                temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector *) * (fb_helper->connector_count + 1), GFP_KERNEL);
@@ -163,11 +181,14 @@ static void remove_from_modeset(struct drm_mode_set *set,
        }
        set->num_connectors--;
 
-       /* because i915 is pissy about this..
+       /*
         * TODO maybe need to makes sure we set it back to !=NULL somewhere?
         */
-       if (set->num_connectors == 0)
+       if (set->num_connectors == 0) {
                set->fb = NULL;
+               drm_mode_destroy(connector->dev, set->mode);
+               set->mode = NULL;
+       }
 }
 
 int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
@@ -176,6 +197,9 @@ int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
        struct drm_fb_helper_connector *fb_helper_connector;
        int i, j;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
 
        for (i = 0; i < fb_helper->connector_count; i++) {
@@ -269,7 +293,7 @@ static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
        struct drm_device *dev = crtc->dev;
        struct drm_crtc *c;
 
-       list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
+       drm_for_each_crtc(c, dev) {
                if (crtc->base.id == c->base.id)
                        return c->primary->fb;
        }
@@ -312,16 +336,84 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
 }
 EXPORT_SYMBOL(drm_fb_helper_debug_leave);
 
-static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
+static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper)
+{
+       struct drm_device *dev = fb_helper->dev;
+       struct drm_plane *plane;
+       struct drm_atomic_state *state;
+       int i, ret;
+       unsigned plane_mask;
+
+       state = drm_atomic_state_alloc(dev);
+       if (!state)
+               return -ENOMEM;
+
+       state->acquire_ctx = dev->mode_config.acquire_ctx;
+retry:
+       plane_mask = 0;
+       drm_for_each_plane(plane, dev) {
+               struct drm_plane_state *plane_state;
+
+               plane_state = drm_atomic_get_plane_state(state, plane);
+               if (IS_ERR(plane_state)) {
+                       ret = PTR_ERR(plane_state);
+                       goto fail;
+               }
+
+               plane_state->rotation = BIT(DRM_ROTATE_0);
+
+               plane->old_fb = plane->fb;
+               plane_mask |= 1 << drm_plane_index(plane);
+
+               /* disable non-primary: */
+               if (plane->type == DRM_PLANE_TYPE_PRIMARY)
+                       continue;
+
+               ret = __drm_atomic_helper_disable_plane(plane, plane_state);
+               if (ret != 0)
+                       goto fail;
+       }
+
+       for(i = 0; i < fb_helper->crtc_count; i++) {
+               struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
+
+               ret = __drm_atomic_helper_set_config(mode_set, state);
+               if (ret != 0)
+                       goto fail;
+       }
+
+       ret = drm_atomic_commit(state);
+
+fail:
+       drm_atomic_clean_old_fb(dev, plane_mask, ret);
+
+       if (ret == -EDEADLK)
+               goto backoff;
+
+       if (ret != 0)
+               drm_atomic_state_free(state);
+
+       return ret;
+
+backoff:
+       drm_atomic_state_clear(state);
+       drm_atomic_legacy_backoff(state);
+
+       goto retry;
+}
+
+static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 {
        struct drm_device *dev = fb_helper->dev;
        struct drm_plane *plane;
-       bool error = false;
        int i;
 
        drm_warn_on_modeset_not_all_locked(dev);
 
-       list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+       if (fb_helper->atomic)
+               return restore_fbdev_mode_atomic(fb_helper);
+
+       drm_for_each_plane(plane, dev) {
                if (plane->type != DRM_PLANE_TYPE_PRIMARY)
                        drm_plane_force_disable(plane);
 
@@ -337,32 +429,22 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
                struct drm_crtc *crtc = mode_set->crtc;
                int ret;
 
-               if (crtc->funcs->cursor_set) {
+               if (crtc->funcs->cursor_set2) {
+                       ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0);
+                       if (ret)
+                               return ret;
+               } else if (crtc->funcs->cursor_set) {
                        ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
                        if (ret)
-                               error = true;
+                               return ret;
                }
 
                ret = drm_mode_set_config_internal(mode_set);
                if (ret)
-                       error = true;
+                       return ret;
        }
-       return error;
-}
-/**
- * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration
- * @fb_helper: fbcon to restore
- *
- * This should be called from driver's drm ->lastclose callback
- * when implementing an fbcon on top of kms using this helper. This ensures that
- * the user isn't greeted with a black screen when e.g. X dies.
- *
- * Use this variant if you need to bypass locking (panic), or already
- * hold all modeset locks.  Otherwise use drm_fb_helper_restore_fbdev_mode_unlocked()
- */
-static bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
-{
-       return restore_fbdev_mode(fb_helper);
+
+       return 0;
 }
 
 /**
@@ -372,12 +454,18 @@ static bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
  * This should be called from driver's drm ->lastclose callback
  * when implementing an fbcon on top of kms using this helper. This ensures that
  * the user isn't greeted with a black screen when e.g. X dies.
+ *
+ * RETURNS:
+ * Zero if everything went ok, negative error code otherwise.
  */
-bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
+int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
 {
        struct drm_device *dev = fb_helper->dev;
-       bool ret;
-       bool do_delayed = false;
+       bool do_delayed;
+       int ret;
+
+       if (!drm_fbdev_emulation)
+               return -ENODEV;
 
        drm_modeset_lock_all(dev);
        ret = restore_fbdev_mode(fb_helper);
@@ -393,6 +481,31 @@ bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
 }
 EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
 
+static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
+{
+       struct drm_device *dev = fb_helper->dev;
+       struct drm_crtc *crtc;
+       int bound = 0, crtcs_bound = 0;
+
+       /* Sometimes user space wants everything disabled, so don't steal the
+        * display if there's a master. */
+       if (dev->primary->master)
+               return false;
+
+       drm_for_each_crtc(crtc, dev) {
+               if (crtc->primary->fb)
+                       crtcs_bound++;
+               if (crtc->primary->fb == fb_helper->fb)
+                       bound++;
+       }
+
+       if (bound < crtcs_bound)
+               return false;
+
+       return true;
+}
+
+#ifdef CONFIG_MAGIC_SYSRQ
 /*
  * restore fbcon display for all kms driver's using this helper, used for sysrq
  * and panic handling.
@@ -411,67 +524,15 @@ static bool drm_fb_helper_force_kernel_mode(void)
                if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
                        continue;
 
-               /*
-                * NOTE: Use trylock mode to avoid deadlocks and sleeping in
-                * panic context.
-                */
-               if (__drm_modeset_lock_all(dev, true) != 0) {
-                       error = true;
-                       continue;
-               }
-
-               ret = drm_fb_helper_restore_fbdev_mode(helper);
+               drm_modeset_lock_all(dev);
+               ret = restore_fbdev_mode(helper);
                if (ret)
                        error = true;
-
                drm_modeset_unlock_all(dev);
        }
        return error;
 }
 
-static int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
-                       void *panic_str)
-{
-       /*
-        * It's a waste of time and effort to switch back to text console
-        * if the kernel should reboot before panic messages can be seen.
-        */
-       if (panic_timeout < 0)
-               return 0;
-
-       pr_err("panic occurred, switching back to text console\n");
-       return drm_fb_helper_force_kernel_mode();
-}
-
-static struct notifier_block paniced = {
-       .notifier_call = drm_fb_helper_panic,
-};
-
-static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
-{
-       struct drm_device *dev = fb_helper->dev;
-       struct drm_crtc *crtc;
-       int bound = 0, crtcs_bound = 0;
-
-       /* Sometimes user space wants everything disabled, so don't steal the
-        * display if there's a master. */
-       if (dev->primary->master)
-               return false;
-
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               if (crtc->primary->fb)
-                       crtcs_bound++;
-               if (crtc->primary->fb == fb_helper->fb)
-                       bound++;
-       }
-
-       if (bound < crtcs_bound)
-               return false;
-
-       return true;
-}
-
-#ifdef CONFIG_MAGIC_SYSRQ
 static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
 {
        bool ret;
@@ -503,14 +564,6 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
        struct drm_connector *connector;
        int i, j;
 
-       /*
-        * fbdev->blank can be called from irq context in case of a panic.
-        * Since we already have our own special panic handler which will
-        * restore the fbdev console mode completely, just bail out early.
-        */
-       if (oops_in_progress)
-               return;
-
        /*
         * For each CRTC in this fb, turn the connectors on/off.
         */
@@ -544,6 +597,9 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
  */
 int drm_fb_helper_blank(int blank, struct fb_info *info)
 {
+       if (oops_in_progress)
+               return -EBUSY;
+
        switch (blank) {
        /* Display: On; HSync: On, VSync: On */
        case FB_BLANK_UNBLANK:
@@ -627,6 +683,9 @@ int drm_fb_helper_init(struct drm_device *dev,
        struct drm_crtc *crtc;
        int i;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        if (!max_conn_count)
                return -EINVAL;
 
@@ -655,11 +714,13 @@ int drm_fb_helper_init(struct drm_device *dev,
        }
 
        i = 0;
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+       drm_for_each_crtc(crtc, dev) {
                fb_helper->crtc_info[i].mode_set.crtc = crtc;
                i++;
        }
 
+       fb_helper->atomic = !!drm_core_check_feature(dev, DRIVER_ATOMIC);
+
        return 0;
 out_free:
        drm_fb_helper_crtc_free(fb_helper);
@@ -667,14 +728,94 @@ out_free:
 }
 EXPORT_SYMBOL(drm_fb_helper_init);
 
+/**
+ * drm_fb_helper_alloc_fbi - allocate fb_info and some of its members
+ * @fb_helper: driver-allocated fbdev helper
+ *
+ * A helper to alloc fb_info and the members cmap and apertures. Called
+ * by the driver within the fb_probe fb_helper callback function.
+ *
+ * RETURNS:
+ * fb_info pointer if things went okay, pointer containing error code
+ * otherwise
+ */
+struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper)
+{
+       struct device *dev = fb_helper->dev->dev;
+       struct fb_info *info;
+       int ret;
+
+       info = framebuffer_alloc(0, dev);
+       if (!info)
+               return ERR_PTR(-ENOMEM);
+
+       ret = fb_alloc_cmap(&info->cmap, 256, 0);
+       if (ret)
+               goto err_release;
+
+       info->apertures = alloc_apertures(1);
+       if (!info->apertures) {
+               ret = -ENOMEM;
+               goto err_free_cmap;
+       }
+
+       fb_helper->fbdev = info;
+
+       return info;
+
+err_free_cmap:
+       fb_dealloc_cmap(&info->cmap);
+err_release:
+       framebuffer_release(info);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(drm_fb_helper_alloc_fbi);
+
+/**
+ * drm_fb_helper_unregister_fbi - unregister fb_info framebuffer device
+ * @fb_helper: driver-allocated fbdev helper
+ *
+ * A wrapper around unregister_framebuffer, to release the fb_info
+ * framebuffer device
+ */
+void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper)
+{
+       if (fb_helper && fb_helper->fbdev)
+               unregister_framebuffer(fb_helper->fbdev);
+}
+EXPORT_SYMBOL(drm_fb_helper_unregister_fbi);
+
+/**
+ * drm_fb_helper_release_fbi - dealloc fb_info and its members
+ * @fb_helper: driver-allocated fbdev helper
+ *
+ * A helper to free memory taken by fb_info and the members cmap and
+ * apertures
+ */
+void drm_fb_helper_release_fbi(struct drm_fb_helper *fb_helper)
+{
+       if (fb_helper) {
+               struct fb_info *info = fb_helper->fbdev;
+
+               if (info) {
+                       if (info->cmap.len)
+                               fb_dealloc_cmap(&info->cmap);
+                       framebuffer_release(info);
+               }
+
+               fb_helper->fbdev = NULL;
+       }
+}
+EXPORT_SYMBOL(drm_fb_helper_release_fbi);
+
 void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
 {
+       if (!drm_fbdev_emulation)
+               return;
+
        if (!list_empty(&fb_helper->kernel_fb_list)) {
                list_del(&fb_helper->kernel_fb_list);
                if (list_empty(&kernel_fb_helper_list)) {
-                       pr_info("drm: unregistered panic notifier\n");
-                       atomic_notifier_chain_unregister(&panic_notifier_list,
-                                                        &paniced);
                        unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
                }
        }
@@ -684,6 +825,149 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
 }
 EXPORT_SYMBOL(drm_fb_helper_fini);
 
+/**
+ * drm_fb_helper_unlink_fbi - wrapper around unlink_framebuffer
+ * @fb_helper: driver-allocated fbdev helper
+ *
+ * A wrapper around unlink_framebuffer implemented by fbdev core
+ */
+void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper)
+{
+       if (fb_helper && fb_helper->fbdev)
+               unlink_framebuffer(fb_helper->fbdev);
+}
+EXPORT_SYMBOL(drm_fb_helper_unlink_fbi);
+
+/**
+ * drm_fb_helper_sys_read - wrapper around fb_sys_read
+ * @info: fb_info struct pointer
+ * @buf: userspace buffer to read from framebuffer memory
+ * @count: number of bytes to read from framebuffer memory
+ * @ppos: read offset within framebuffer memory
+ *
+ * A wrapper around fb_sys_read implemented by fbdev core
+ */
+ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
+                              size_t count, loff_t *ppos)
+{
+       return fb_sys_read(info, buf, count, ppos);
+}
+EXPORT_SYMBOL(drm_fb_helper_sys_read);
+
+/**
+ * drm_fb_helper_sys_write - wrapper around fb_sys_write
+ * @info: fb_info struct pointer
+ * @buf: userspace buffer to write to framebuffer memory
+ * @count: number of bytes to write to framebuffer memory
+ * @ppos: write offset within framebuffer memory
+ *
+ * A wrapper around fb_sys_write implemented by fbdev core
+ */
+ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       return fb_sys_write(info, buf, count, ppos);
+}
+EXPORT_SYMBOL(drm_fb_helper_sys_write);
+
+/**
+ * drm_fb_helper_sys_fillrect - wrapper around sys_fillrect
+ * @info: fbdev registered by the helper
+ * @rect: info about rectangle to fill
+ *
+ * A wrapper around sys_fillrect implemented by fbdev core
+ */
+void drm_fb_helper_sys_fillrect(struct fb_info *info,
+                               const struct fb_fillrect *rect)
+{
+       sys_fillrect(info, rect);
+}
+EXPORT_SYMBOL(drm_fb_helper_sys_fillrect);
+
+/**
+ * drm_fb_helper_sys_copyarea - wrapper around sys_copyarea
+ * @info: fbdev registered by the helper
+ * @area: info about area to copy
+ *
+ * A wrapper around sys_copyarea implemented by fbdev core
+ */
+void drm_fb_helper_sys_copyarea(struct fb_info *info,
+                               const struct fb_copyarea *area)
+{
+       sys_copyarea(info, area);
+}
+EXPORT_SYMBOL(drm_fb_helper_sys_copyarea);
+
+/**
+ * drm_fb_helper_sys_imageblit - wrapper around sys_imageblit
+ * @info: fbdev registered by the helper
+ * @image: info about image to blit
+ *
+ * A wrapper around sys_imageblit implemented by fbdev core
+ */
+void drm_fb_helper_sys_imageblit(struct fb_info *info,
+                                const struct fb_image *image)
+{
+       sys_imageblit(info, image);
+}
+EXPORT_SYMBOL(drm_fb_helper_sys_imageblit);
+
+/**
+ * drm_fb_helper_cfb_fillrect - wrapper around cfb_fillrect
+ * @info: fbdev registered by the helper
+ * @rect: info about rectangle to fill
+ *
+ * A wrapper around cfb_imageblit implemented by fbdev core
+ */
+void drm_fb_helper_cfb_fillrect(struct fb_info *info,
+                               const struct fb_fillrect *rect)
+{
+       cfb_fillrect(info, rect);
+}
+EXPORT_SYMBOL(drm_fb_helper_cfb_fillrect);
+
+/**
+ * drm_fb_helper_cfb_copyarea - wrapper around cfb_copyarea
+ * @info: fbdev registered by the helper
+ * @area: info about area to copy
+ *
+ * A wrapper around cfb_copyarea implemented by fbdev core
+ */
+void drm_fb_helper_cfb_copyarea(struct fb_info *info,
+                               const struct fb_copyarea *area)
+{
+       cfb_copyarea(info, area);
+}
+EXPORT_SYMBOL(drm_fb_helper_cfb_copyarea);
+
+/**
+ * drm_fb_helper_cfb_imageblit - wrapper around cfb_imageblit
+ * @info: fbdev registered by the helper
+ * @image: info about image to blit
+ *
+ * A wrapper around cfb_imageblit implemented by fbdev core
+ */
+void drm_fb_helper_cfb_imageblit(struct fb_info *info,
+                                const struct fb_image *image)
+{
+       cfb_imageblit(info, image);
+}
+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
+ *
+ * A wrapper around fb_set_suspend implemented by fbdev core
+ */
+void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, int state)
+{
+       if (fb_helper && fb_helper->fbdev)
+               fb_set_suspend(fb_helper->fbdev, state);
+}
+EXPORT_SYMBOL(drm_fb_helper_set_suspend);
+
 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
                     u16 blue, u16 regno, struct fb_info *info)
 {
@@ -771,9 +1055,10 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
        int i, j, rc = 0;
        int start;
 
-       if (__drm_modeset_lock_all(dev, !!oops_in_progress)) {
+       if (oops_in_progress)
                return -EBUSY;
-       }
+
+       drm_modeset_lock_all(dev);
        if (!drm_fb_helper_is_bound(fb_helper)) {
                drm_modeset_unlock_all(dev);
                return -EBUSY;
@@ -922,6 +1207,9 @@ int drm_fb_helper_set_par(struct fb_info *info)
        struct drm_fb_helper *fb_helper = info->par;
        struct fb_var_screeninfo *var = &info->var;
 
+       if (oops_in_progress)
+               return -EBUSY;
+
        if (var->pixclock != 0) {
                DRM_ERROR("PIXEL CLOCK SET\n");
                return -EINVAL;
@@ -933,6 +1221,66 @@ int drm_fb_helper_set_par(struct fb_info *info)
 }
 EXPORT_SYMBOL(drm_fb_helper_set_par);
 
+static int pan_display_atomic(struct fb_var_screeninfo *var,
+                             struct fb_info *info)
+{
+       struct drm_fb_helper *fb_helper = info->par;
+       struct drm_device *dev = fb_helper->dev;
+       struct drm_atomic_state *state;
+       struct drm_plane *plane;
+       int i, ret;
+       unsigned plane_mask;
+
+       state = drm_atomic_state_alloc(dev);
+       if (!state)
+               return -ENOMEM;
+
+       state->acquire_ctx = dev->mode_config.acquire_ctx;
+retry:
+       plane_mask = 0;
+       for(i = 0; i < fb_helper->crtc_count; i++) {
+               struct drm_mode_set *mode_set;
+
+               mode_set = &fb_helper->crtc_info[i].mode_set;
+
+               mode_set->x = var->xoffset;
+               mode_set->y = var->yoffset;
+
+               ret = __drm_atomic_helper_set_config(mode_set, state);
+               if (ret != 0)
+                       goto fail;
+
+               plane = mode_set->crtc->primary;
+               plane_mask |= drm_plane_index(plane);
+               plane->old_fb = plane->fb;
+       }
+
+       ret = drm_atomic_commit(state);
+       if (ret != 0)
+               goto fail;
+
+       info->var.xoffset = var->xoffset;
+       info->var.yoffset = var->yoffset;
+
+
+fail:
+       drm_atomic_clean_old_fb(dev, plane_mask, ret);
+
+       if (ret == -EDEADLK)
+               goto backoff;
+
+       if (ret != 0)
+               drm_atomic_state_free(state);
+
+       return ret;
+
+backoff:
+       drm_atomic_state_clear(state);
+       drm_atomic_legacy_backoff(state);
+
+       goto retry;
+}
+
 /**
  * drm_fb_helper_pan_display - implementation for ->fb_pan_display
  * @var: updated screen information
@@ -947,14 +1295,20 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
        int ret = 0;
        int i;
 
-       if (__drm_modeset_lock_all(dev, !!oops_in_progress)) {
+       if (oops_in_progress)
                return -EBUSY;
-       }
+
+       drm_modeset_lock_all(dev);
        if (!drm_fb_helper_is_bound(fb_helper)) {
                drm_modeset_unlock_all(dev);
                return -EBUSY;
        }
 
+       if (fb_helper->atomic) {
+               ret = pan_display_atomic(var, info);
+               goto unlock;
+       }
+
        for (i = 0; i < fb_helper->crtc_count; i++) {
                modeset = &fb_helper->crtc_info[i].mode_set;
 
@@ -969,6 +1323,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
                        }
                }
        }
+unlock:
        drm_modeset_unlock_all(dev);
        return ret;
 }
@@ -1109,12 +1464,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
        dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n",
                        info->node, info->fix.id);
 
-       /* Switch back to kernel console on panic */
-       /* multi card linked list maybe */
        if (list_empty(&kernel_fb_helper_list)) {
-               dev_info(fb_helper->dev->dev, "registered panic notifier\n");
-               atomic_notifier_chain_register(&panic_notifier_list,
-                                              &paniced);
                register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
        }
 
@@ -1749,6 +2099,9 @@ int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
        struct drm_device *dev = fb_helper->dev;
        int count = 0;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        mutex_lock(&dev->mode_config.mutex);
        count = drm_fb_helper_probe_connector_modes(fb_helper,
                                                    dev->mode_config.max_width,
@@ -1792,6 +2145,9 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
        struct drm_device *dev = fb_helper->dev;
        u32 max_width, max_height;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        mutex_lock(&fb_helper->dev->mode_config.mutex);
        if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
                fb_helper->delayed_hotplug = true;