staging: drm/omap: call omap_gem_roll() in non-atomic ctx
authorRob Clark <rob@ti.com>
Mon, 5 Mar 2012 16:48:33 +0000 (10:48 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 7 Mar 2012 21:38:07 +0000 (13:38 -0800)
If fbcon calls us from atomic context, push the work off to the
workqueue to avoid calling into the gem/dmm code in an atomic
context.

Signed-off-by: Rob Clark <rob@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/omapdrm/omap_fbdev.c
drivers/staging/omapdrm/omap_gem.c

index 96940bbfc6f433b833cc935e42f566f246e7c0d4..11acd4c35ed2c0d043cb8b45a12e5a36ca87460e 100644 (file)
@@ -37,6 +37,9 @@ struct omap_fbdev {
        struct drm_framebuffer *fb;
        struct drm_gem_object *bo;
        bool ywrap_enabled;
+
+       /* for deferred dmm roll when getting called in atomic ctx */
+       struct work_struct work;
 };
 
 static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h);
@@ -75,12 +78,22 @@ static void omap_fbdev_imageblit(struct fb_info *fbi,
                                image->width, image->height);
 }
 
+static void pan_worker(struct work_struct *work)
+{
+       struct omap_fbdev *fbdev = container_of(work, struct omap_fbdev, work);
+       struct fb_info *fbi = fbdev->base.fbdev;
+       int npages;
+
+       /* DMM roll shifts in 4K pages: */
+       npages = fbi->fix.line_length >> PAGE_SHIFT;
+       omap_gem_roll(fbdev->bo, fbi->var.yoffset * npages);
+}
+
 static int omap_fbdev_pan_display(struct fb_var_screeninfo *var,
                struct fb_info *fbi)
 {
        struct drm_fb_helper *helper = get_fb(fbi);
        struct omap_fbdev *fbdev = to_omap_fbdev(helper);
-       int npages;
 
        if (!helper)
                goto fallback;
@@ -88,9 +101,12 @@ static int omap_fbdev_pan_display(struct fb_var_screeninfo *var,
        if (!fbdev->ywrap_enabled)
                goto fallback;
 
-       /* DMM roll shifts in 4K pages: */
-       npages = fbi->fix.line_length >> PAGE_SHIFT;
-       omap_gem_roll(fbdev->bo, var->yoffset * npages);
+       if (drm_can_sleep()) {
+               pan_worker(&fbdev->work);
+       } else {
+               struct omap_drm_private *priv = helper->dev->dev_private;
+               queue_work(priv->wq, &fbdev->work);
+       }
 
        return 0;
 
@@ -336,6 +352,8 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
                goto fail;
        }
 
+       INIT_WORK(&fbdev->work, pan_worker);
+
        helper = &fbdev->base;
 
        helper->funcs = &omap_fb_helper_funcs;
index b7d6f886c5cf7ca9d412cf5bf9dce0ac427e4f0b..bd35520d50ffc4cde3646ddc9369c6a0689c27da 100644 (file)
@@ -566,6 +566,8 @@ fail:
 
 /* Set scrolling position.  This allows us to implement fast scrolling
  * for console.
+ *
+ * Call only from non-atomic contexts.
  */
 int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll)
 {
@@ -580,18 +582,6 @@ int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll)
 
        omap_obj->roll = roll;
 
-       if (in_atomic() || mutex_is_locked(&obj->dev->struct_mutex)) {
-               /* this can get called from fbcon in atomic context.. so
-                * just ignore it and wait for next time called from
-                * interruptible context to update the PAT.. the result
-                * may be that user sees wrap-around instead of scrolling
-                * momentarily on the screen.  If we wanted to be fancier
-                * we could perhaps schedule some workqueue work at this
-                * point.
-                */
-               return 0;
-       }
-
        mutex_lock(&obj->dev->struct_mutex);
 
        /* if we aren't mapped yet, we don't need to do anything */