Merge commit '06d1ee32a4d25356a710b49d5e95dbdd68bdf505' of git://git.kernel.org/pub...
[deliverable/linux.git] / drivers / gpu / drm / drm_fb_helper.c
index ca08c472311bd3f6238f7513bc4ac26737228884..bd6d4ab2751250ab9af68f0aa9f91e0cb44463ce 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);
 
@@ -99,6 +106,9 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
        struct drm_connector *connector;
        int i;
 
+       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;
@@ -129,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);
@@ -184,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++) {
@@ -320,15 +336,96 @@ 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;
+
+       state = drm_atomic_state_alloc(dev);
+       if (!state)
+               return -ENOMEM;
+
+       state->acquire_ctx = dev->mode_config.acquire_ctx;
+retry:
+       drm_for_each_plane(plane, dev) {
+               struct drm_plane_state *plane_state;
+
+               plane->old_fb = plane->fb;
+
+               plane_state = drm_atomic_get_plane_state(state, plane);
+               if (IS_ERR(plane_state)) {
+                       ret = PTR_ERR(plane_state);
+                       goto fail;
+               }
+
+               ret = drm_atomic_plane_set_property(plane, plane_state,
+                               dev->mode_config.rotation_property,
+                               BIT(DRM_ROTATE_0));
+               if (ret != 0)
+                       goto fail;
+
+               /* 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_for_each_plane(plane, dev) {
+               if (ret == 0) {
+                       struct drm_framebuffer *new_fb = plane->state->fb;
+                       if (new_fb)
+                               drm_framebuffer_reference(new_fb);
+                       plane->fb = new_fb;
+                       plane->crtc = plane->state->crtc;
+
+                       if (plane->old_fb)
+                               drm_framebuffer_unreference(plane->old_fb);
+               }
+               plane->old_fb = NULL;
+       }
+
+       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);
 
+       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);
@@ -348,18 +445,19 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
                if (crtc->funcs->cursor_set2) {
                        ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0);
                        if (ret)
-                               error = true;
+                               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;
+
+       return 0;
 }
 
 /**
@@ -369,12 +467,18 @@ static bool 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);
@@ -592,6 +696,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;
 
@@ -625,6 +732,8 @@ int drm_fb_helper_init(struct drm_device *dev,
                i++;
        }
 
+       fb_helper->atomic = !!drm_core_check_feature(dev, DRIVER_ATOMIC);
+
        return 0;
 out_free:
        drm_fb_helper_crtc_free(fb_helper);
@@ -714,6 +823,9 @@ 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)) {
@@ -1122,6 +1234,57 @@ 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;
+       int i, ret;
+
+       state = drm_atomic_state_alloc(dev);
+       if (!state)
+               return -ENOMEM;
+
+       state->acquire_ctx = dev->mode_config.acquire_ctx;
+retry:
+       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;
+       }
+
+       ret = drm_atomic_commit(state);
+       if (ret != 0)
+               goto fail;
+
+       info->var.xoffset = var->xoffset;
+       info->var.yoffset = var->yoffset;
+
+       return 0;
+
+fail:
+       if (ret == -EDEADLK)
+               goto backoff;
+
+       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
@@ -1145,6 +1308,11 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
                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;
 
@@ -1159,6 +1327,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
                        }
                }
        }
+unlock:
        drm_modeset_unlock_all(dev);
        return ret;
 }
@@ -1934,6 +2103,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,
@@ -1977,6 +2149,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;
This page took 0.036651 seconds and 5 git commands to generate.