drm/nouveau: disallow fbcon accel if running in interrupt context
authorBen Skeggs <bskeggs@redhat.com>
Tue, 5 Oct 2010 06:41:29 +0000 (16:41 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Fri, 3 Dec 2010 05:04:20 +0000 (15:04 +1000)
A future commit will add locking to the DRM's channel, and there's numerous
problems that come up if we allow printk from an interrupt context to be
accelerated.  It seems saner to just disallow it completely.

As a nice side-effect, all the "to accel or not to accel" logic gets moved
out of the chipset-specific code.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nouveau_fbcon.c
drivers/gpu/drm/nouveau/nouveau_fbcon.h
drivers/gpu/drm/nouveau/nv04_fbcon.c
drivers/gpu/drm/nouveau/nv50_fbcon.c

index 02a4d1fd484560efe66f6262f140b3983f3e6241..22e83adcc930a26b73e9f6abc83d349adf61247e 100644 (file)
 #include "nouveau_fbcon.h"
 #include "nouveau_dma.h"
 
+static void
+nouveau_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+       struct nouveau_fbdev *nfbdev = info->par;
+       struct drm_device *dev = nfbdev->dev;
+       struct drm_nouveau_private *dev_priv = dev->dev_private;
+       int ret;
+
+       if (info->state != FBINFO_STATE_RUNNING)
+               return;
+
+       ret = -ENODEV;
+       if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED)) {
+               if (dev_priv->card_type < NV_50)
+                       ret = nv04_fbcon_fillrect(info, rect);
+               else
+               if (dev_priv->card_type < NV_C0)
+                       ret = nv50_fbcon_fillrect(info, rect);
+       }
+
+       if (ret == 0)
+               return;
+
+       if (ret != -ENODEV)
+               nouveau_fbcon_gpu_lockup(info);
+       cfb_fillrect(info, rect);
+}
+
+static void
+nouveau_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *image)
+{
+       struct nouveau_fbdev *nfbdev = info->par;
+       struct drm_device *dev = nfbdev->dev;
+       struct drm_nouveau_private *dev_priv = dev->dev_private;
+       int ret;
+
+       if (info->state != FBINFO_STATE_RUNNING)
+               return;
+
+       ret = -ENODEV;
+       if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED)) {
+               if (dev_priv->card_type < NV_50)
+                       ret = nv04_fbcon_copyarea(info, image);
+               else
+               if (dev_priv->card_type < NV_C0)
+                       ret = nv50_fbcon_copyarea(info, image);
+       }
+
+       if (ret == 0)
+               return;
+
+       if (ret != -ENODEV)
+               nouveau_fbcon_gpu_lockup(info);
+       cfb_copyarea(info, image);
+}
+
+static void
+nouveau_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+       struct nouveau_fbdev *nfbdev = info->par;
+       struct drm_device *dev = nfbdev->dev;
+       struct drm_nouveau_private *dev_priv = dev->dev_private;
+       int ret;
+
+       if (info->state != FBINFO_STATE_RUNNING)
+               return;
+
+       ret = -ENODEV;
+       if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED)) {
+               if (dev_priv->card_type < NV_50)
+                       ret = nv04_fbcon_imageblit(info, image);
+               else
+               if (dev_priv->card_type < NV_C0)
+                       ret = nv50_fbcon_imageblit(info, image);
+       }
+
+       if (ret == 0)
+               return;
+
+       if (ret != -ENODEV)
+               nouveau_fbcon_gpu_lockup(info);
+       cfb_imageblit(info, image);
+}
+
 static int
 nouveau_fbcon_sync(struct fb_info *info)
 {
@@ -97,24 +181,9 @@ static struct fb_ops nouveau_fbcon_ops = {
        .owner = THIS_MODULE,
        .fb_check_var = drm_fb_helper_check_var,
        .fb_set_par = drm_fb_helper_set_par,
-       .fb_fillrect = cfb_fillrect,
-       .fb_copyarea = cfb_copyarea,
-       .fb_imageblit = cfb_imageblit,
-       .fb_sync = nouveau_fbcon_sync,
-       .fb_pan_display = drm_fb_helper_pan_display,
-       .fb_blank = drm_fb_helper_blank,
-       .fb_setcmap = drm_fb_helper_setcmap,
-       .fb_debug_enter = drm_fb_helper_debug_enter,
-       .fb_debug_leave = drm_fb_helper_debug_leave,
-};
-
-static struct fb_ops nv04_fbcon_ops = {
-       .owner = THIS_MODULE,
-       .fb_check_var = drm_fb_helper_check_var,
-       .fb_set_par = drm_fb_helper_set_par,
-       .fb_fillrect = nv04_fbcon_fillrect,
-       .fb_copyarea = nv04_fbcon_copyarea,
-       .fb_imageblit = nv04_fbcon_imageblit,
+       .fb_fillrect = nouveau_fbcon_fillrect,
+       .fb_copyarea = nouveau_fbcon_copyarea,
+       .fb_imageblit = nouveau_fbcon_imageblit,
        .fb_sync = nouveau_fbcon_sync,
        .fb_pan_display = drm_fb_helper_pan_display,
        .fb_blank = drm_fb_helper_blank,
@@ -123,14 +192,13 @@ static struct fb_ops nv04_fbcon_ops = {
        .fb_debug_leave = drm_fb_helper_debug_leave,
 };
 
-static struct fb_ops nv50_fbcon_ops = {
+static struct fb_ops nouveau_fbcon_sw_ops = {
        .owner = THIS_MODULE,
        .fb_check_var = drm_fb_helper_check_var,
        .fb_set_par = drm_fb_helper_set_par,
-       .fb_fillrect = nv50_fbcon_fillrect,
-       .fb_copyarea = nv50_fbcon_copyarea,
-       .fb_imageblit = nv50_fbcon_imageblit,
-       .fb_sync = nouveau_fbcon_sync,
+       .fb_fillrect = cfb_fillrect,
+       .fb_copyarea = cfb_copyarea,
+       .fb_imageblit = cfb_imageblit,
        .fb_pan_display = drm_fb_helper_pan_display,
        .fb_blank = drm_fb_helper_blank,
        .fb_setcmap = drm_fb_helper_setcmap,
@@ -257,7 +325,7 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev,
                              FBINFO_HWACCEL_FILLRECT |
                              FBINFO_HWACCEL_IMAGEBLIT;
        info->flags |= FBINFO_CAN_FORCE_OUTPUT;
-       info->fbops = &nouveau_fbcon_ops;
+       info->fbops = &nouveau_fbcon_sw_ops;
        info->fix.smem_start = dev->mode_config.fb_base + nvbo->bo.offset -
                               dev_priv->vm_vram_base;
        info->fix.smem_len = size;
@@ -286,18 +354,15 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev,
        info->pixmap.scan_align = 1;
 
        if (dev_priv->channel && !nouveau_nofbaccel) {
-               switch (dev_priv->card_type) {
-               case NV_C0:
-                       break;
-               case NV_50:
-                       nv50_fbcon_accel_init(info);
-                       info->fbops = &nv50_fbcon_ops;
-                       break;
-               default:
-                       nv04_fbcon_accel_init(info);
-                       info->fbops = &nv04_fbcon_ops;
-                       break;
-               };
+               ret = -ENODEV;
+               if (dev_priv->card_type < NV_50)
+                       ret = nv04_fbcon_accel_init(info);
+               else
+               if (dev_priv->card_type < NV_C0)
+                       ret = nv50_fbcon_accel_init(info);
+
+               if (ret == 0)
+                       info->fbops = &nouveau_fbcon_ops;
        }
 
        nouveau_fbcon_zfill(dev, nfbdev);
index e7e12684c37e6d08858af1bf6483004357850043..6b933f2c3a5bba608aeb5bcb9659d802fe3eb761 100644 (file)
@@ -40,13 +40,13 @@ struct nouveau_fbdev {
 
 void nouveau_fbcon_restore(void);
 
-void nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region);
-void nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
-void nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image);
+int nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region);
+int nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
+int nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image);
 int nv04_fbcon_accel_init(struct fb_info *info);
-void nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
-void nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region);
-void nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image);
+int nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
+int nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region);
+int nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image);
 int nv50_fbcon_accel_init(struct fb_info *info);
 
 void nouveau_fbcon_gpu_lockup(struct fb_info *info);
index 33e4c9388bc1b8f787f5bc89e7350670098a7722..a32804e7d2026bdb33dddbd97fac9c679f687419 100644 (file)
 #include "nouveau_ramht.h"
 #include "nouveau_fbcon.h"
 
-void
+int
 nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
 {
        struct nouveau_fbdev *nfbdev = info->par;
        struct drm_device *dev = nfbdev->dev;
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nouveau_channel *chan = dev_priv->channel;
+       int ret;
 
-       if (info->state != FBINFO_STATE_RUNNING)
-               return;
-
-       if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 4)) {
-               nouveau_fbcon_gpu_lockup(info);
-       }
-
-       if (info->flags & FBINFO_HWACCEL_DISABLED) {
-               cfb_copyarea(info, region);
-               return;
-       }
+       ret = RING_SPACE(chan, 4);
+       if (ret)
+               return ret;
 
        BEGIN_RING(chan, NvSubImageBlit, 0x0300, 3);
        OUT_RING(chan, (region->sy << 16) | region->sx);
        OUT_RING(chan, (region->dy << 16) | region->dx);
        OUT_RING(chan, (region->height << 16) | region->width);
        FIRE_RING(chan);
+       return 0;
 }
 
-void
+int
 nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
 {
        struct nouveau_fbdev *nfbdev = info->par;
        struct drm_device *dev = nfbdev->dev;
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nouveau_channel *chan = dev_priv->channel;
+       int ret;
 
-       if (info->state != FBINFO_STATE_RUNNING)
-               return;
-
-       if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 7)) {
-               nouveau_fbcon_gpu_lockup(info);
-       }
-
-       if (info->flags & FBINFO_HWACCEL_DISABLED) {
-               cfb_fillrect(info, rect);
-               return;
-       }
+       ret = RING_SPACE(chan, 7);
+       if (ret)
+               return ret;
 
        BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1);
        OUT_RING(chan, (rect->rop != ROP_COPY) ? 1 : 3);
@@ -87,9 +74,10 @@ nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
        OUT_RING(chan, (rect->dx << 16) | rect->dy);
        OUT_RING(chan, (rect->width << 16) | rect->height);
        FIRE_RING(chan);
+       return 0;
 }
 
-void
+int
 nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
 {
        struct nouveau_fbdev *nfbdev = info->par;
@@ -101,23 +89,14 @@ nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
        uint32_t dsize;
        uint32_t width;
        uint32_t *data = (uint32_t *)image->data;
+       int ret;
 
-       if (info->state != FBINFO_STATE_RUNNING)
-               return;
-
-       if (image->depth != 1) {
-               cfb_imageblit(info, image);
-               return;
-       }
-
-       if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 8)) {
-               nouveau_fbcon_gpu_lockup(info);
-       }
+       if (image->depth != 1)
+               return -ENODEV;
 
-       if (info->flags & FBINFO_HWACCEL_DISABLED) {
-               cfb_imageblit(info, image);
-               return;
-       }
+       ret = RING_SPACE(chan, 8);
+       if (ret)
+               return ret;
 
        width = ALIGN(image->width, 8);
        dsize = ALIGN(width * image->height, 32) >> 5;
@@ -144,11 +123,9 @@ nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
        while (dsize) {
                int iter_len = dsize > 128 ? 128 : dsize;
 
-               if (RING_SPACE(chan, iter_len + 1)) {
-                       nouveau_fbcon_gpu_lockup(info);
-                       cfb_imageblit(info, image);
-                       return;
-               }
+               ret = RING_SPACE(chan, iter_len + 1);
+               if (ret)
+                       return ret;
 
                BEGIN_RING(chan, NvSubGdiRect, 0x0c00, iter_len);
                OUT_RINGp(chan, data, iter_len);
@@ -157,6 +134,7 @@ nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
        }
 
        FIRE_RING(chan);
+       return 0;
 }
 
 static int
index 6dcf048eddbc7130b753b0e64f6de65d7f10887d..6edf9dca35cad346e810239f3b048197ab335de1 100644 (file)
@@ -4,26 +4,18 @@
 #include "nouveau_ramht.h"
 #include "nouveau_fbcon.h"
 
-void
+int
 nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
 {
        struct nouveau_fbdev *nfbdev = info->par;
        struct drm_device *dev = nfbdev->dev;
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nouveau_channel *chan = dev_priv->channel;
+       int ret;
 
-       if (info->state != FBINFO_STATE_RUNNING)
-               return;
-
-       if (!(info->flags & FBINFO_HWACCEL_DISABLED) &&
-            RING_SPACE(chan, rect->rop == ROP_COPY ? 7 : 11)) {
-               nouveau_fbcon_gpu_lockup(info);
-       }
-
-       if (info->flags & FBINFO_HWACCEL_DISABLED) {
-               cfb_fillrect(info, rect);
-               return;
-       }
+       ret = RING_SPACE(chan, rect->rop == ROP_COPY ? 7 : 11);
+       if (ret)
+               return ret;
 
        if (rect->rop != ROP_COPY) {
                BEGIN_RING(chan, NvSub2D, 0x02ac, 1);
@@ -45,27 +37,21 @@ nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
                OUT_RING(chan, 3);
        }
        FIRE_RING(chan);
+       return 0;
 }
 
-void
+int
 nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
 {
        struct nouveau_fbdev *nfbdev = info->par;
        struct drm_device *dev = nfbdev->dev;
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nouveau_channel *chan = dev_priv->channel;
+       int ret;
 
-       if (info->state != FBINFO_STATE_RUNNING)
-               return;
-
-       if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 12)) {
-               nouveau_fbcon_gpu_lockup(info);
-       }
-
-       if (info->flags & FBINFO_HWACCEL_DISABLED) {
-               cfb_copyarea(info, region);
-               return;
-       }
+       ret = RING_SPACE(chan, 12);
+       if (ret)
+               return ret;
 
        BEGIN_RING(chan, NvSub2D, 0x0110, 1);
        OUT_RING(chan, 0);
@@ -80,9 +66,10 @@ nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
        OUT_RING(chan, 0);
        OUT_RING(chan, region->sy);
        FIRE_RING(chan);
+       return 0;
 }
 
-void
+int
 nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
 {
        struct nouveau_fbdev *nfbdev = info->par;
@@ -92,23 +79,14 @@ nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
        uint32_t width, dwords, *data = (uint32_t *)image->data;
        uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel));
        uint32_t *palette = info->pseudo_palette;
+       int ret;
 
-       if (info->state != FBINFO_STATE_RUNNING)
-               return;
-
-       if (image->depth != 1) {
-               cfb_imageblit(info, image);
-               return;
-       }
-
-       if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 11)) {
-               nouveau_fbcon_gpu_lockup(info);
-       }
+       if (image->depth != 1)
+               return -ENODEV;
 
-       if (info->flags & FBINFO_HWACCEL_DISABLED) {
-               cfb_imageblit(info, image);
-               return;
-       }
+       ret = RING_SPACE(chan, 11);
+       if (ret)
+               return ret;
 
        width = ALIGN(image->width, 32);
        dwords = (width * image->height) >> 5;
@@ -134,11 +112,9 @@ nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
        while (dwords) {
                int push = dwords > 2047 ? 2047 : dwords;
 
-               if (RING_SPACE(chan, push + 1)) {
-                       nouveau_fbcon_gpu_lockup(info);
-                       cfb_imageblit(info, image);
-                       return;
-               }
+               ret = RING_SPACE(chan, push + 1);
+               if (ret)
+                       return ret;
 
                dwords -= push;
 
@@ -148,6 +124,7 @@ nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
        }
 
        FIRE_RING(chan);
+       return 0;
 }
 
 int