#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)
 {
        .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,
        .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,
                              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;
        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);
 
 
 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);
 
 #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);
        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;
        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;
        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);
        }
 
        FIRE_RING(chan);
+       return 0;
 }
 
 static int
 
 #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);
                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);
        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;
        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;
        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;
 
        }
 
        FIRE_RING(chan);
+       return 0;
 }
 
 int