Home | History | Annotate | Line # | Download | only in nouveau
nouveau_fbcon.c revision 1.1.1.2.6.3
      1 /*	$NetBSD: nouveau_fbcon.c,v 1.1.1.2.6.3 2017/12/03 11:37:52 jdolecek Exp $	*/
      2 
      3 /*
      4  * Copyright  2007 David Airlie
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice (including the next
     14  * paragraph) shall be included in all copies or substantial portions of the
     15  * Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     23  * DEALINGS IN THE SOFTWARE.
     24  *
     25  * Authors:
     26  *     David Airlie
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: nouveau_fbcon.c,v 1.1.1.2.6.3 2017/12/03 11:37:52 jdolecek Exp $");
     31 
     32 #include <linux/module.h>
     33 #include <linux/kernel.h>
     34 #include <linux/errno.h>
     35 #include <linux/string.h>
     36 #include <linux/mm.h>
     37 #include <linux/tty.h>
     38 #include <linux/sysrq.h>
     39 #include <linux/delay.h>
     40 #include <linux/fb.h>
     41 #include <linux/init.h>
     42 #include <linux/screen_info.h>
     43 #include <linux/vga_switcheroo.h>
     44 #include <linux/console.h>
     45 
     46 #include <drm/drmP.h>
     47 #include <drm/drm_crtc.h>
     48 #include <drm/drm_crtc_helper.h>
     49 #include <drm/drm_fb_helper.h>
     50 
     51 #include "nouveau_drm.h"
     52 #include "nouveau_gem.h"
     53 #include "nouveau_bo.h"
     54 #include "nouveau_fbcon.h"
     55 #include "nouveau_chan.h"
     56 
     57 #include "nouveau_crtc.h"
     58 
     59 #ifdef __NetBSD__
     60 #include "nouveaufb.h"
     61 #endif
     62 
     63 #include <core/client.h>
     64 #include <core/device.h>
     65 
     66 #include <subdev/fb.h>
     67 
     68 #ifdef __NetBSD__		/* XXX nouveau fbaccel */
     69 static const int nouveau_nofbaccel = 1;
     70 #else
     71 MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration");
     72 static int nouveau_nofbaccel = 0;
     73 module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400);
     74 
     75 static void
     76 nouveau_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
     77 {
     78 	struct nouveau_fbdev *fbcon = info->par;
     79 	struct nouveau_drm *drm = nouveau_drm(fbcon->dev);
     80 	struct nouveau_device *device = nv_device(drm->device);
     81 	int ret;
     82 
     83 	if (info->state != FBINFO_STATE_RUNNING)
     84 		return;
     85 
     86 	ret = -ENODEV;
     87 	if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
     88 	    mutex_trylock(&drm->client.mutex)) {
     89 		if (device->card_type < NV_50)
     90 			ret = nv04_fbcon_fillrect(info, rect);
     91 		else
     92 		if (device->card_type < NV_C0)
     93 			ret = nv50_fbcon_fillrect(info, rect);
     94 		else
     95 			ret = nvc0_fbcon_fillrect(info, rect);
     96 		mutex_unlock(&drm->client.mutex);
     97 	}
     98 
     99 	if (ret == 0)
    100 		return;
    101 
    102 	if (ret != -ENODEV)
    103 		nouveau_fbcon_gpu_lockup(info);
    104 	cfb_fillrect(info, rect);
    105 }
    106 
    107 static void
    108 nouveau_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *image)
    109 {
    110 	struct nouveau_fbdev *fbcon = info->par;
    111 	struct nouveau_drm *drm = nouveau_drm(fbcon->dev);
    112 	struct nouveau_device *device = nv_device(drm->device);
    113 	int ret;
    114 
    115 	if (info->state != FBINFO_STATE_RUNNING)
    116 		return;
    117 
    118 	ret = -ENODEV;
    119 	if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
    120 	    mutex_trylock(&drm->client.mutex)) {
    121 		if (device->card_type < NV_50)
    122 			ret = nv04_fbcon_copyarea(info, image);
    123 		else
    124 		if (device->card_type < NV_C0)
    125 			ret = nv50_fbcon_copyarea(info, image);
    126 		else
    127 			ret = nvc0_fbcon_copyarea(info, image);
    128 		mutex_unlock(&drm->client.mutex);
    129 	}
    130 
    131 	if (ret == 0)
    132 		return;
    133 
    134 	if (ret != -ENODEV)
    135 		nouveau_fbcon_gpu_lockup(info);
    136 	cfb_copyarea(info, image);
    137 }
    138 
    139 static void
    140 nouveau_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
    141 {
    142 	struct nouveau_fbdev *fbcon = info->par;
    143 	struct nouveau_drm *drm = nouveau_drm(fbcon->dev);
    144 	struct nouveau_device *device = nv_device(drm->device);
    145 	int ret;
    146 
    147 	if (info->state != FBINFO_STATE_RUNNING)
    148 		return;
    149 
    150 	ret = -ENODEV;
    151 	if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
    152 	    mutex_trylock(&drm->client.mutex)) {
    153 		if (device->card_type < NV_50)
    154 			ret = nv04_fbcon_imageblit(info, image);
    155 		else
    156 		if (device->card_type < NV_C0)
    157 			ret = nv50_fbcon_imageblit(info, image);
    158 		else
    159 			ret = nvc0_fbcon_imageblit(info, image);
    160 		mutex_unlock(&drm->client.mutex);
    161 	}
    162 
    163 	if (ret == 0)
    164 		return;
    165 
    166 	if (ret != -ENODEV)
    167 		nouveau_fbcon_gpu_lockup(info);
    168 	cfb_imageblit(info, image);
    169 }
    170 
    171 static int
    172 nouveau_fbcon_sync(struct fb_info *info)
    173 {
    174 	struct nouveau_fbdev *fbcon = info->par;
    175 	struct nouveau_drm *drm = nouveau_drm(fbcon->dev);
    176 	struct nouveau_channel *chan = drm->channel;
    177 	int ret;
    178 
    179 	if (!chan || !chan->accel_done || in_interrupt() ||
    180 	    info->state != FBINFO_STATE_RUNNING ||
    181 	    info->flags & FBINFO_HWACCEL_DISABLED)
    182 		return 0;
    183 
    184 	if (!mutex_trylock(&drm->client.mutex))
    185 		return 0;
    186 
    187 	ret = nouveau_channel_idle(chan);
    188 	mutex_unlock(&drm->client.mutex);
    189 	if (ret) {
    190 		nouveau_fbcon_gpu_lockup(info);
    191 		return 0;
    192 	}
    193 
    194 	chan->accel_done = false;
    195 	return 0;
    196 }
    197 
    198 static struct fb_ops nouveau_fbcon_ops = {
    199 	.owner = THIS_MODULE,
    200 	.fb_check_var = drm_fb_helper_check_var,
    201 	.fb_set_par = drm_fb_helper_set_par,
    202 	.fb_fillrect = nouveau_fbcon_fillrect,
    203 	.fb_copyarea = nouveau_fbcon_copyarea,
    204 	.fb_imageblit = nouveau_fbcon_imageblit,
    205 	.fb_sync = nouveau_fbcon_sync,
    206 	.fb_pan_display = drm_fb_helper_pan_display,
    207 	.fb_blank = drm_fb_helper_blank,
    208 	.fb_setcmap = drm_fb_helper_setcmap,
    209 	.fb_debug_enter = drm_fb_helper_debug_enter,
    210 	.fb_debug_leave = drm_fb_helper_debug_leave,
    211 };
    212 
    213 static struct fb_ops nouveau_fbcon_sw_ops = {
    214 	.owner = THIS_MODULE,
    215 	.fb_check_var = drm_fb_helper_check_var,
    216 	.fb_set_par = drm_fb_helper_set_par,
    217 	.fb_fillrect = cfb_fillrect,
    218 	.fb_copyarea = cfb_copyarea,
    219 	.fb_imageblit = cfb_imageblit,
    220 	.fb_pan_display = drm_fb_helper_pan_display,
    221 	.fb_blank = drm_fb_helper_blank,
    222 	.fb_setcmap = drm_fb_helper_setcmap,
    223 	.fb_debug_enter = drm_fb_helper_debug_enter,
    224 	.fb_debug_leave = drm_fb_helper_debug_leave,
    225 };
    226 #endif	/* XXX nouveau fbaccel */
    227 
    228 static void nouveau_fbcon_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
    229 				    u16 blue, int regno)
    230 {
    231 	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
    232 
    233 	nv_crtc->lut.r[regno] = red;
    234 	nv_crtc->lut.g[regno] = green;
    235 	nv_crtc->lut.b[regno] = blue;
    236 }
    237 
    238 static void nouveau_fbcon_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
    239 				    u16 *blue, int regno)
    240 {
    241 	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
    242 
    243 	*red = nv_crtc->lut.r[regno];
    244 	*green = nv_crtc->lut.g[regno];
    245 	*blue = nv_crtc->lut.b[regno];
    246 }
    247 
    248 static void
    249 nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *fbcon)
    250 {
    251 #ifdef __NetBSD__		/* XXX nouveau fbaccel */
    252 	struct nouveau_bo *const nvbo = fbcon->nouveau_fb.nvbo;
    253 
    254 	(void)memset(__UNVOLATILE(nvbo_kmap_obj_iovirtual(nvbo)), 0,
    255 	    nvbo->bo.num_pages << PAGE_SHIFT);
    256 #else
    257 	struct fb_info *info = fbcon->helper.fbdev;
    258 	struct fb_fillrect rect;
    259 
    260 	/* Clear the entire fbcon.  The drm will program every connector
    261 	 * with it's preferred mode.  If the sizes differ, one display will
    262 	 * quite likely have garbage around the console.
    263 	 */
    264 	rect.dx = rect.dy = 0;
    265 	rect.width = info->var.xres_virtual;
    266 	rect.height = info->var.yres_virtual;
    267 	rect.color = 0;
    268 	rect.rop = ROP_COPY;
    269 	info->fbops->fb_fillrect(info, &rect);
    270 #endif
    271 }
    272 
    273 static int
    274 nouveau_fbcon_create(struct drm_fb_helper *helper,
    275 		     struct drm_fb_helper_surface_size *sizes)
    276 {
    277 	struct nouveau_fbdev *fbcon = (struct nouveau_fbdev *)helper;
    278 	struct drm_device *dev = fbcon->dev;
    279 	struct nouveau_drm *drm = nouveau_drm(dev);
    280 	struct nouveau_device *device = nv_device(drm->device);
    281 #ifndef __NetBSD__
    282 	struct fb_info *info;
    283 #endif
    284 	struct drm_framebuffer *fb;
    285 	struct nouveau_framebuffer *nouveau_fb;
    286 	struct nouveau_channel *chan;
    287 	struct nouveau_bo *nvbo;
    288 	struct drm_mode_fb_cmd2 mode_cmd;
    289 #ifndef __NetBSD__
    290 	struct pci_dev *pdev = dev->pdev;
    291 #endif
    292 	int size, ret;
    293 
    294 	mode_cmd.width = sizes->surface_width;
    295 	mode_cmd.height = sizes->surface_height;
    296 
    297 	mode_cmd.pitches[0] = mode_cmd.width * (sizes->surface_bpp >> 3);
    298 	mode_cmd.pitches[0] = roundup(mode_cmd.pitches[0], 256);
    299 
    300 	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
    301 							  sizes->surface_depth);
    302 
    303 	size = mode_cmd.pitches[0] * mode_cmd.height;
    304 	size = roundup(size, PAGE_SIZE);
    305 
    306 	ret = nouveau_gem_new(dev, size, 0, NOUVEAU_GEM_DOMAIN_VRAM,
    307 			      0, 0x0000, &nvbo);
    308 	if (ret) {
    309 		NV_ERROR(drm, "failed to allocate framebuffer\n");
    310 		goto out;
    311 	}
    312 
    313 	ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM);
    314 	if (ret) {
    315 		NV_ERROR(drm, "failed to pin fb: %d\n", ret);
    316 		goto out_unref;
    317 	}
    318 
    319 	ret = nouveau_bo_map(nvbo);
    320 	if (ret) {
    321 		NV_ERROR(drm, "failed to map fb: %d\n", ret);
    322 		goto out_unpin;
    323 	}
    324 
    325 	chan = nouveau_nofbaccel ? NULL : drm->channel;
    326 	if (chan && device->card_type >= NV_50) {
    327 		ret = nouveau_bo_vma_add(nvbo, nv_client(chan->cli)->vm,
    328 					&fbcon->nouveau_fb.vma);
    329 		if (ret) {
    330 			NV_ERROR(drm, "failed to map fb into chan: %d\n", ret);
    331 			chan = NULL;
    332 		}
    333 	}
    334 
    335 #ifdef __NetBSD__
    336 	nouveau_framebuffer_init(dev, &fbcon->nouveau_fb, &mode_cmd, nvbo);
    337 	nouveau_fb = &fbcon->nouveau_fb;
    338 	fb = &nouveau_fb->base;
    339 
    340 	nouveau_fbcon_zfill(dev, fbcon);
    341 
    342     {
    343 	static const struct nouveaufb_attach_args zero_nfa;
    344 	struct nouveaufb_attach_args nfa = zero_nfa;
    345 
    346 	nfa.nfa_fb_helper = helper;
    347 	nfa.nfa_fb_sizes = *sizes;
    348 	nfa.nfa_fb_ptr = nvbo_kmap_obj_iovirtual(nvbo);
    349 	nfa.nfa_fb_linebytes = mode_cmd.pitches[0];
    350 
    351 	helper->fbdev = config_found_ia(dev->dev, "nouveaufbbus", &nfa, NULL);
    352 	if (helper->fbdev == NULL) {
    353 		DRM_ERROR("failed to attach nouveaufb\n");
    354 		goto out_unlock;
    355 	}
    356     }
    357 	helper->fb = fb;
    358 
    359 	return 0;
    360 #else
    361 	mutex_lock(&dev->struct_mutex);
    362 
    363 	info = framebuffer_alloc(0, &pdev->dev);
    364 	if (!info) {
    365 		ret = -ENOMEM;
    366 		goto out_unlock;
    367 	}
    368 
    369 	ret = fb_alloc_cmap(&info->cmap, 256, 0);
    370 	if (ret) {
    371 		ret = -ENOMEM;
    372 		framebuffer_release(info);
    373 		goto out_unlock;
    374 	}
    375 
    376 	info->par = fbcon;
    377 
    378 	nouveau_framebuffer_init(dev, &fbcon->nouveau_fb, &mode_cmd, nvbo);
    379 
    380 	nouveau_fb = &fbcon->nouveau_fb;
    381 	fb = &nouveau_fb->base;
    382 
    383 	/* setup helper */
    384 	fbcon->helper.fb = fb;
    385 	fbcon->helper.fbdev = info;
    386 
    387 	strcpy(info->fix.id, "nouveaufb");
    388 	if (!chan)
    389 		info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_DISABLED;
    390 	else
    391 		info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
    392 			      FBINFO_HWACCEL_FILLRECT |
    393 			      FBINFO_HWACCEL_IMAGEBLIT;
    394 	info->flags |= FBINFO_CAN_FORCE_OUTPUT;
    395 	info->fbops = &nouveau_fbcon_sw_ops;
    396 	info->fix.smem_start = nvbo->bo.mem.bus.base +
    397 			       nvbo->bo.mem.bus.offset;
    398 	info->fix.smem_len = size;
    399 
    400 	info->screen_base = nvbo_kmap_obj_iovirtual(nouveau_fb->nvbo);
    401 	info->screen_size = size;
    402 
    403 	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
    404 	drm_fb_helper_fill_var(info, &fbcon->helper, sizes->fb_width, sizes->fb_height);
    405 
    406 	/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
    407 
    408 	mutex_unlock(&dev->struct_mutex);
    409 
    410 	if (chan) {
    411 		ret = -ENODEV;
    412 		if (device->card_type < NV_50)
    413 			ret = nv04_fbcon_accel_init(info);
    414 		else
    415 		if (device->card_type < NV_C0)
    416 			ret = nv50_fbcon_accel_init(info);
    417 		else
    418 			ret = nvc0_fbcon_accel_init(info);
    419 
    420 		if (ret == 0)
    421 			info->fbops = &nouveau_fbcon_ops;
    422 	}
    423 
    424 	nouveau_fbcon_zfill(dev, fbcon);
    425 
    426 	/* To allow resizeing without swapping buffers */
    427 	NV_INFO(drm, "allocated %dx%d fb: 0x%lx, bo %p\n",
    428 		nouveau_fb->base.width, nouveau_fb->base.height,
    429 		nvbo->bo.offset, nvbo);
    430 
    431 	vga_switcheroo_client_fb_set(dev->pdev, info);
    432 	return 0;
    433 #endif	/* defined(__NetBSD__) */
    434 
    435 out_unlock:
    436 #ifndef __NetBSD__
    437 	mutex_unlock(&dev->struct_mutex);
    438 #endif
    439 	if (chan)
    440 		nouveau_bo_vma_del(nvbo, &fbcon->nouveau_fb.vma);
    441 	nouveau_bo_unmap(nvbo);
    442 out_unpin:
    443 	nouveau_bo_unpin(nvbo);
    444 out_unref:
    445 	nouveau_bo_ref(NULL, &nvbo);
    446 out:
    447 	return ret;
    448 }
    449 
    450 void
    451 nouveau_fbcon_output_poll_changed(struct drm_device *dev)
    452 {
    453 	struct nouveau_drm *drm = nouveau_drm(dev);
    454 	if (drm->fbcon)
    455 		drm_fb_helper_hotplug_event(&drm->fbcon->helper);
    456 }
    457 
    458 static int
    459 nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon)
    460 {
    461 	struct nouveau_framebuffer *nouveau_fb = &fbcon->nouveau_fb;
    462 #ifndef __NetBSD__
    463 	struct fb_info *info;
    464 #endif
    465 
    466 	if (fbcon->helper.fbdev) {
    467 #ifdef __NetBSD__
    468 		int ret;
    469 
    470 		/* XXX errno NetBSD->Linux */
    471 		ret = -config_detach(fbcon->helper.fbdev, DETACH_FORCE);
    472 		if (ret)
    473 			DRM_ERROR("failed to detach nouveaufb: %d\n", ret);
    474 		fbcon->helper.fbdev = NULL;
    475 #else
    476 		info = fbcon->helper.fbdev;
    477 		unregister_framebuffer(info);
    478 		if (info->cmap.len)
    479 			fb_dealloc_cmap(&info->cmap);
    480 		framebuffer_release(info);
    481 #endif
    482 	}
    483 
    484 	if (nouveau_fb->nvbo) {
    485 		nouveau_bo_unmap(nouveau_fb->nvbo);
    486 		nouveau_bo_vma_del(nouveau_fb->nvbo, &nouveau_fb->vma);
    487 		nouveau_bo_unpin(nouveau_fb->nvbo);
    488 		drm_gem_object_unreference_unlocked(&nouveau_fb->nvbo->gem);
    489 		nouveau_fb->nvbo = NULL;
    490 	}
    491 	drm_fb_helper_fini(&fbcon->helper);
    492 	drm_framebuffer_unregister_private(&nouveau_fb->base);
    493 	drm_framebuffer_cleanup(&nouveau_fb->base);
    494 	return 0;
    495 }
    496 
    497 #ifndef __NetBSD__		/* XXX nouveau fbaccel */
    498 void nouveau_fbcon_gpu_lockup(struct fb_info *info)
    499 {
    500 	struct nouveau_fbdev *fbcon = info->par;
    501 	struct nouveau_drm *drm = nouveau_drm(fbcon->dev);
    502 
    503 	NV_ERROR(drm, "GPU lockup - switching to software fbcon\n");
    504 	info->flags |= FBINFO_HWACCEL_DISABLED;
    505 }
    506 #endif
    507 
    508 static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
    509 	.gamma_set = nouveau_fbcon_gamma_set,
    510 	.gamma_get = nouveau_fbcon_gamma_get,
    511 	.fb_probe = nouveau_fbcon_create,
    512 };
    513 
    514 
    515 int
    516 nouveau_fbcon_init(struct drm_device *dev)
    517 {
    518 	struct nouveau_drm *drm = nouveau_drm(dev);
    519 	struct nouveau_fb *pfb = nouveau_fb(drm->device);
    520 	struct nouveau_fbdev *fbcon;
    521 	int preferred_bpp;
    522 	int ret;
    523 
    524 	if (!dev->mode_config.num_crtc ||
    525 	    (dev->pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
    526 		return 0;
    527 
    528 	fbcon = kzalloc(sizeof(struct nouveau_fbdev), GFP_KERNEL);
    529 	if (!fbcon)
    530 		return -ENOMEM;
    531 
    532 	fbcon->dev = dev;
    533 	drm->fbcon = fbcon;
    534 	fbcon->helper.funcs = &nouveau_fbcon_helper_funcs;
    535 
    536 	ret = drm_fb_helper_init(dev, &fbcon->helper,
    537 				 dev->mode_config.num_crtc, 4);
    538 	if (ret) {
    539 		kfree(fbcon);
    540 		return ret;
    541 	}
    542 
    543 	drm_fb_helper_single_add_all_connectors(&fbcon->helper);
    544 
    545 	if (pfb->ram->size <= 32 * 1024 * 1024)
    546 		preferred_bpp = 8;
    547 	else
    548 	if (pfb->ram->size <= 64 * 1024 * 1024)
    549 		preferred_bpp = 16;
    550 	else
    551 		preferred_bpp = 32;
    552 
    553 	/* disable all the possible outputs/crtcs before entering KMS mode */
    554 	drm_helper_disable_unused_functions(dev);
    555 
    556 	drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp);
    557 	return 0;
    558 }
    559 
    560 void
    561 nouveau_fbcon_fini(struct drm_device *dev)
    562 {
    563 	struct nouveau_drm *drm = nouveau_drm(dev);
    564 
    565 	if (!drm->fbcon)
    566 		return;
    567 
    568 	nouveau_fbcon_destroy(dev, drm->fbcon);
    569 	kfree(drm->fbcon);
    570 	drm->fbcon = NULL;
    571 }
    572 
    573 void
    574 nouveau_fbcon_save_disable_accel(struct drm_device *dev)
    575 {
    576 #ifndef __NetBSD__		/* XXX nouveau fbaccel */
    577 	struct nouveau_drm *drm = nouveau_drm(dev);
    578 	if (drm->fbcon) {
    579 		drm->fbcon->saved_flags = drm->fbcon->helper.fbdev->flags;
    580 		drm->fbcon->helper.fbdev->flags |= FBINFO_HWACCEL_DISABLED;
    581 	}
    582 #endif
    583 }
    584 
    585 void
    586 nouveau_fbcon_restore_accel(struct drm_device *dev)
    587 {
    588 #ifndef __NetBSD__		/* XXX nouveau fbaccel */
    589 	struct nouveau_drm *drm = nouveau_drm(dev);
    590 	if (drm->fbcon) {
    591 		drm->fbcon->helper.fbdev->flags = drm->fbcon->saved_flags;
    592 	}
    593 #endif
    594 }
    595 
    596 void
    597 nouveau_fbcon_set_suspend(struct drm_device *dev, int state)
    598 {
    599 #ifndef __NetBSD__
    600 	struct nouveau_drm *drm = nouveau_drm(dev);
    601 	if (drm->fbcon) {
    602 		console_lock();
    603 		if (state == 1)
    604 			nouveau_fbcon_save_disable_accel(dev);
    605 		fb_set_suspend(drm->fbcon->helper.fbdev, state);
    606 		if (state == 0)
    607 			nouveau_fbcon_restore_accel(dev);
    608 		console_unlock();
    609 	}
    610 #endif
    611 }
    612 
    613 void
    614 nouveau_fbcon_zfill_all(struct drm_device *dev)
    615 {
    616 	struct nouveau_drm *drm = nouveau_drm(dev);
    617 	if (drm->fbcon) {
    618 		nouveau_fbcon_zfill(dev, drm->fbcon);
    619 	}
    620 }
    621