1 | /* $NetBSD: nouveau_fbcon.c,v 1.2 2015/03/06 01:43:07 riastradh 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.2 2015/03/06 01:43:07 riastradh 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 | |