projects/linux·blob master · refs

802 lines · 18.1 KiB

1/*
2 * linux/drivers/video/fbmem.c
3 *
4 * Copyright (C) 1994 Martin Schaller
5 *
6 * 2001 - Documented with DocBook
7 * - Brad Douglas <[email protected]>
8 *
9 * This file is subject to the terms and conditions of the GNU General Public
10 * License. See the file COPYING in the main directory of this archive
11 * for more details.
12 */
13
14#include <linux/console.h>
15#include <linux/export.h>
16#include <linux/fb.h>
17#include <linux/lcd.h>
18#include <linux/leds.h>
19
20#include <video/nomodeset.h>
21
22#include "fb_internal.h"
23#include "fbcon.h"
24
25 /*
26 * Frame buffer device initialization and setup routines
27 */
28
29#define FBPIXMAPSIZE (1024 * 8)
30
31struct class *fb_class;
32
33DEFINE_MUTEX(registration_lock);
34struct fb_info *registered_fb[FB_MAX] __read_mostly;
35int num_registered_fb __read_mostly;
36#define for_each_registered_fb(i) \
37 for (i = 0; i < FB_MAX; i++) \
38 if (!registered_fb[i]) {} else
39
40struct fb_info *get_fb_info(unsigned int idx)
41{
42 struct fb_info *fb_info;
43
44 if (idx >= FB_MAX)
45 return ERR_PTR(-ENODEV);
46
47 mutex_lock(&registration_lock);
48 fb_info = registered_fb[idx];
49 if (fb_info)
50 refcount_inc(&fb_info->count);
51 mutex_unlock(&registration_lock);
52
53 return fb_info;
54}
55
56void put_fb_info(struct fb_info *fb_info)
57{
58 if (!refcount_dec_and_test(&fb_info->count))
59 return;
60 if (fb_info->fbops->fb_destroy)
61 fb_info->fbops->fb_destroy(fb_info);
62}
63
64/*
65 * Helpers
66 */
67
68int fb_get_color_depth(struct fb_var_screeninfo *var,
69 struct fb_fix_screeninfo *fix)
70{
71 int depth = 0;
72
73 if (fix->visual == FB_VISUAL_MONO01 ||
74 fix->visual == FB_VISUAL_MONO10)
75 depth = 1;
76 else {
77 if (var->green.length == var->blue.length &&
78 var->green.length == var->red.length &&
79 var->green.offset == var->blue.offset &&
80 var->green.offset == var->red.offset)
81 depth = var->green.length;
82 else
83 depth = var->green.length + var->red.length +
84 var->blue.length;
85 }
86
87 return depth;
88}
89EXPORT_SYMBOL(fb_get_color_depth);
90
91/*
92 * Data padding functions.
93 */
94void fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, const u8 *src, u32 s_pitch, u32 height)
95{
96 __fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, height);
97}
98EXPORT_SYMBOL(fb_pad_aligned_buffer);
99
100void fb_pad_unaligned_buffer(u8 *dst, u32 d_pitch, const u8 *src, u32 idx, u32 height,
101 u32 shift_high, u32 shift_low, u32 mod)
102{
103 u8 mask = (u8) (0xff << shift_high), tmp;
104 int i, j;
105
106 for (i = height; i--; ) {
107 for (j = 0; j < idx; j++) {
108 tmp = dst[j];
109 tmp &= mask;
110 tmp |= *src >> shift_low;
111 dst[j] = tmp;
112 tmp = *src << shift_high;
113 dst[j+1] = tmp;
114 src++;
115 }
116 tmp = dst[idx];
117 tmp &= mask;
118 tmp |= *src >> shift_low;
119 dst[idx] = tmp;
120 if (shift_high < mod) {
121 tmp = *src << shift_high;
122 dst[idx+1] = tmp;
123 }
124 src++;
125 dst += d_pitch;
126 }
127}
128EXPORT_SYMBOL(fb_pad_unaligned_buffer);
129
130/*
131 * we need to lock this section since fb_cursor
132 * may use fb_imageblit()
133 */
134char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size)
135{
136 u32 align = buf->buf_align - 1, offset;
137 char *addr = buf->addr;
138
139 /* If IO mapped, we need to sync before access, no sharing of
140 * the pixmap is done
141 */
142 if (buf->flags & FB_PIXMAP_IO) {
143 if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
144 info->fbops->fb_sync(info);
145 return addr;
146 }
147
148 /* See if we fit in the remaining pixmap space */
149 offset = buf->offset + align;
150 offset &= ~align;
151 if (offset + size > buf->size) {
152 /* We do not fit. In order to be able to re-use the buffer,
153 * we must ensure no asynchronous DMA'ing or whatever operation
154 * is in progress, we sync for that.
155 */
156 if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
157 info->fbops->fb_sync(info);
158 offset = 0;
159 }
160 buf->offset = offset + size;
161 addr += offset;
162
163 return addr;
164}
165EXPORT_SYMBOL(fb_get_buffer_offset);
166
167int
168fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
169{
170 struct fb_fix_screeninfo *fix = &info->fix;
171 unsigned int yres = info->var.yres;
172 int err = 0;
173
174 if (var->yoffset > 0) {
175 if (var->vmode & FB_VMODE_YWRAP) {
176 if (!fix->ywrapstep || (var->yoffset % fix->ywrapstep))
177 err = -EINVAL;
178 else
179 yres = 0;
180 } else if (!fix->ypanstep || (var->yoffset % fix->ypanstep))
181 err = -EINVAL;
182 }
183
184 if (var->xoffset > 0 && (!fix->xpanstep ||
185 (var->xoffset % fix->xpanstep)))
186 err = -EINVAL;
187
188 if (err || !info->fbops->fb_pan_display ||
189 var->yoffset > info->var.yres_virtual - yres ||
190 var->xoffset > info->var.xres_virtual - info->var.xres)
191 return -EINVAL;
192
193 if ((err = info->fbops->fb_pan_display(var, info)))
194 return err;
195 info->var.xoffset = var->xoffset;
196 info->var.yoffset = var->yoffset;
197 if (var->vmode & FB_VMODE_YWRAP)
198 info->var.vmode |= FB_VMODE_YWRAP;
199 else
200 info->var.vmode &= ~FB_VMODE_YWRAP;
201 return 0;
202}
203EXPORT_SYMBOL(fb_pan_display);
204
205static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var,
206 u32 activate)
207{
208 struct fb_blit_caps caps, fbcaps;
209 int err = 0;
210
211 memset(&caps, 0, sizeof(caps));
212 memset(&fbcaps, 0, sizeof(fbcaps));
213 caps.flags = (activate & FB_ACTIVATE_ALL) ? 1 : 0;
214 fbcon_get_requirement(info, &caps);
215 info->fbops->fb_get_caps(info, &fbcaps, var);
216
217 if (!bitmap_subset(caps.x, fbcaps.x, FB_MAX_BLIT_WIDTH) ||
218 !bitmap_subset(caps.y, fbcaps.y, FB_MAX_BLIT_HEIGHT) ||
219 (fbcaps.len < caps.len))
220 err = -EINVAL;
221
222 return err;
223}
224
225static void fb_lcd_notify_mode_change(struct fb_info *info,
226 struct fb_videomode *mode)
227{
228 lcd_notify_mode_change_all(info->device, mode->xres, mode->yres);
229}
230
231int
232fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
233{
234 int ret = 0;
235 u32 activate;
236 struct fb_var_screeninfo old_var;
237 struct fb_videomode mode;
238 u32 unused;
239
240 if (var->activate & FB_ACTIVATE_INV_MODE) {
241 struct fb_videomode mode1, mode2;
242
243 fb_var_to_videomode(&mode1, var);
244 fb_var_to_videomode(&mode2, &info->var);
245 /* make sure we don't delete the videomode of current var */
246 ret = fb_mode_is_equal(&mode1, &mode2);
247 if (!ret) {
248 ret = fbcon_mode_deleted(info, &mode1);
249 if (!ret) {
250 if (info->mode && fb_mode_is_equal(info->mode, &mode1))
251 info->mode = NULL;
252 fb_delete_videomode(&mode1, &info->modelist);
253 }
254 }
255
256 return ret ? -EINVAL : 0;
257 }
258
259 if (!(var->activate & FB_ACTIVATE_FORCE) &&
260 !memcmp(&info->var, var, sizeof(struct fb_var_screeninfo)))
261 return 0;
262
263 activate = var->activate;
264
265 /* When using FOURCC mode, make sure the red, green, blue and
266 * transp fields are set to 0.
267 */
268 if ((info->fix.capabilities & FB_CAP_FOURCC) &&
269 var->grayscale > 1) {
270 if (var->red.offset || var->green.offset ||
271 var->blue.offset || var->transp.offset ||
272 var->red.length || var->green.length ||
273 var->blue.length || var->transp.length ||
274 var->red.msb_right || var->green.msb_right ||
275 var->blue.msb_right || var->transp.msb_right)
276 return -EINVAL;
277 }
278
279 if (!info->fbops->fb_check_var) {
280 *var = info->var;
281 return 0;
282 }
283
284 /* bitfill_aligned() assumes that it's at least 8x8 */
285 if (var->xres < 8 || var->yres < 8)
286 return -EINVAL;
287
288 /* Too huge resolution causes multiplication overflow. */
289 if (check_mul_overflow(var->xres, var->yres, &unused) ||
290 check_mul_overflow(var->xres_virtual, var->yres_virtual, &unused))
291 return -EINVAL;
292
293 ret = info->fbops->fb_check_var(var, info);
294
295 if (ret)
296 return ret;
297
298 /* verify that virtual resolution >= physical resolution */
299 if (var->xres_virtual < var->xres ||
300 var->yres_virtual < var->yres) {
301 pr_warn("WARNING: fbcon: Driver '%s' missed to adjust virtual screen size (%ux%u vs. %ux%u)\n",
302 info->fix.id,
303 var->xres_virtual, var->yres_virtual,
304 var->xres, var->yres);
305 return -EINVAL;
306 }
307
308 if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
309 return 0;
310
311 if (info->fbops->fb_get_caps) {
312 ret = fb_check_caps(info, var, activate);
313
314 if (ret)
315 return ret;
316 }
317
318 old_var = info->var;
319 info->var = *var;
320
321 if (info->fbops->fb_set_par) {
322 ret = info->fbops->fb_set_par(info);
323
324 if (ret) {
325 info->var = old_var;
326 printk(KERN_WARNING "detected "
327 "fb_set_par error, "
328 "error code: %d\n", ret);
329 return ret;
330 }
331 }
332
333 fb_pan_display(info, &info->var);
334 fb_set_cmap(&info->cmap, info);
335 fb_var_to_videomode(&mode, &info->var);
336
337 if (info->modelist.prev && info->modelist.next &&
338 !list_empty(&info->modelist))
339 ret = fb_add_videomode(&mode, &info->modelist);
340
341 if (ret) {
342 info->var = old_var;
343 return ret;
344 }
345
346 fb_lcd_notify_mode_change(info, &mode);
347
348 return 0;
349}
350EXPORT_SYMBOL(fb_set_var);
351
352int fb_set_var_from_user(struct fb_info *info, struct fb_var_screeninfo *var)
353{
354 int ret = fbcon_modechange_possible(info, var);
355
356 if (!ret)
357 ret = fb_set_var(info, var);
358 if (!ret)
359 fbcon_update_vcs(info, var->activate & FB_ACTIVATE_ALL);
360
361 return ret;
362}
363EXPORT_SYMBOL(fb_set_var_from_user);
364
365static void fb_lcd_notify_blank(struct fb_info *info)
366{
367 int power;
368
369 switch (info->blank) {
370 case FB_BLANK_UNBLANK:
371 power = LCD_POWER_ON;
372 break;
373 /* deprecated; TODO: should become 'off' */
374 case FB_BLANK_NORMAL:
375 power = LCD_POWER_REDUCED;
376 break;
377 case FB_BLANK_VSYNC_SUSPEND:
378 power = LCD_POWER_REDUCED_VSYNC_SUSPEND;
379 break;
380 /* 'off' */
381 case FB_BLANK_HSYNC_SUSPEND:
382 case FB_BLANK_POWERDOWN:
383 default:
384 power = LCD_POWER_OFF;
385 break;
386 }
387
388 lcd_notify_blank_all(info->device, power);
389}
390
391static void fb_ledtrig_backlight_notify_blank(struct fb_info *info)
392{
393 if (info->blank == FB_BLANK_UNBLANK)
394 ledtrig_backlight_blank(false);
395 else
396 ledtrig_backlight_blank(true);
397}
398
399int fb_blank(struct fb_info *info, int blank)
400{
401 int old_blank = info->blank;
402 int ret;
403
404 if (!info->fbops->fb_blank)
405 return -EINVAL;
406
407 if (blank > FB_BLANK_POWERDOWN)
408 blank = FB_BLANK_POWERDOWN;
409
410 info->blank = blank;
411
412 ret = info->fbops->fb_blank(blank, info);
413 if (ret)
414 goto err;
415
416 fb_bl_notify_blank(info, old_blank);
417 fb_lcd_notify_blank(info);
418 fb_ledtrig_backlight_notify_blank(info);
419
420 return 0;
421
422err:
423 info->blank = old_blank;
424 return ret;
425}
426EXPORT_SYMBOL(fb_blank);
427
428int fb_blank_from_user(struct fb_info *info, int blank)
429{
430 int ret = fb_blank(info, blank);
431
432 /* might again call into fb_blank */
433 fbcon_fb_blanked(info, blank);
434
435 return ret;
436}
437
438static int fb_check_foreignness(struct fb_info *fi)
439{
440 const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
441
442 fi->flags &= ~FBINFO_FOREIGN_ENDIAN;
443
444#ifdef __BIG_ENDIAN
445 fi->flags |= foreign_endian ? 0 : FBINFO_BE_MATH;
446#else
447 fi->flags |= foreign_endian ? FBINFO_BE_MATH : 0;
448#endif /* __BIG_ENDIAN */
449
450 if (fi->flags & FBINFO_BE_MATH && !fb_be_math(fi)) {
451 pr_err("%s: enable CONFIG_FB_BIG_ENDIAN to "
452 "support this framebuffer\n", fi->fix.id);
453 return -ENOSYS;
454 } else if (!(fi->flags & FBINFO_BE_MATH) && fb_be_math(fi)) {
455 pr_err("%s: enable CONFIG_FB_LITTLE_ENDIAN to "
456 "support this framebuffer\n", fi->fix.id);
457 return -ENOSYS;
458 }
459
460 return 0;
461}
462
463static int do_register_framebuffer(struct fb_info *fb_info)
464{
465 int i, err = 0;
466 struct fb_videomode mode;
467
468 if (fb_check_foreignness(fb_info))
469 return -ENOSYS;
470
471 if (num_registered_fb == FB_MAX)
472 return -ENXIO;
473
474 for (i = 0 ; i < FB_MAX; i++)
475 if (!registered_fb[i])
476 break;
477
478 if (i >= FB_MAX)
479 return -ENXIO;
480
481 if (!fb_info->modelist.prev || !fb_info->modelist.next)
482 INIT_LIST_HEAD(&fb_info->modelist);
483
484 fb_var_to_videomode(&mode, &fb_info->var);
485 err = fb_add_videomode(&mode, &fb_info->modelist);
486 if (err < 0)
487 return err;
488
489 fb_info->node = i;
490 refcount_set(&fb_info->count, 1);
491 mutex_init(&fb_info->lock);
492 mutex_init(&fb_info->mm_lock);
493
494 /*
495 * With an fb_blank callback present, we assume that the
496 * display is blank, so that fb_blank() enables it on the
497 * first modeset.
498 */
499 if (fb_info->fbops->fb_blank)
500 fb_info->blank = FB_BLANK_POWERDOWN;
501
502 fb_device_create(fb_info);
503
504 if (fb_info->pixmap.addr == NULL) {
505 fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
506 if (fb_info->pixmap.addr) {
507 fb_info->pixmap.size = FBPIXMAPSIZE;
508 fb_info->pixmap.buf_align = 1;
509 fb_info->pixmap.scan_align = 1;
510 fb_info->pixmap.access_align = 32;
511 fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
512 }
513 }
514 fb_info->pixmap.offset = 0;
515
516 if (bitmap_empty(fb_info->pixmap.blit_x, FB_MAX_BLIT_WIDTH))
517 bitmap_fill(fb_info->pixmap.blit_x, FB_MAX_BLIT_WIDTH);
518
519 if (bitmap_empty(fb_info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT))
520 bitmap_fill(fb_info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT);
521
522 if (fb_info->skip_vt_switch)
523 pm_vt_switch_required(fb_info->device, false);
524 else
525 pm_vt_switch_required(fb_info->device, true);
526
527 num_registered_fb++;
528 registered_fb[i] = fb_info;
529
530#ifdef CONFIG_GUMSTIX_AM200EPD
531 {
532 struct fb_event event;
533 event.info = fb_info;
534 fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
535 }
536#endif
537
538 return fbcon_fb_registered(fb_info);
539}
540
541static void unbind_console(struct fb_info *fb_info)
542{
543 int i = fb_info->node;
544
545 if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info))
546 return;
547
548 fbcon_fb_unbind(fb_info);
549}
550
551static void unlink_framebuffer(struct fb_info *fb_info)
552{
553 int i;
554
555 i = fb_info->node;
556 if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info))
557 return;
558
559 fb_device_destroy(fb_info);
560 pm_vt_switch_unregister(fb_info->device);
561 unbind_console(fb_info);
562}
563
564static void do_unregister_framebuffer(struct fb_info *fb_info)
565{
566 unlink_framebuffer(fb_info);
567 if (fb_info->pixmap.addr &&
568 (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) {
569 kfree(fb_info->pixmap.addr);
570 fb_info->pixmap.addr = NULL;
571 }
572
573 fbcon_delete_modelist(&fb_info->modelist);
574 fb_destroy_modelist(&fb_info->modelist);
575 registered_fb[fb_info->node] = NULL;
576 num_registered_fb--;
577#ifdef CONFIG_GUMSTIX_AM200EPD
578 {
579 struct fb_event event;
580 event.info = fb_info;
581 fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
582 }
583#endif
584 fbcon_fb_unregistered(fb_info);
585
586 /* this may free fb info */
587 put_fb_info(fb_info);
588}
589
590/**
591 * register_framebuffer - registers a frame buffer device
592 * @fb_info: frame buffer info structure
593 *
594 * Registers a frame buffer device @fb_info.
595 *
596 * Returns negative errno on error, or zero for success.
597 *
598 */
599int
600register_framebuffer(struct fb_info *fb_info)
601{
602 int ret;
603
604 mutex_lock(&registration_lock);
605 ret = do_register_framebuffer(fb_info);
606 mutex_unlock(&registration_lock);
607
608 return ret;
609}
610EXPORT_SYMBOL(register_framebuffer);
611
612/**
613 * unregister_framebuffer - releases a frame buffer device
614 * @fb_info: frame buffer info structure
615 *
616 * Unregisters a frame buffer device @fb_info.
617 *
618 * Returns negative errno on error, or zero for success.
619 *
620 * This function will also notify the framebuffer console
621 * to release the driver.
622 *
623 * This is meant to be called within a driver's module_exit()
624 * function. If this is called outside module_exit(), ensure
625 * that the driver implements fb_open() and fb_release() to
626 * check that no processes are using the device.
627 */
628void
629unregister_framebuffer(struct fb_info *fb_info)
630{
631 mutex_lock(&registration_lock);
632 do_unregister_framebuffer(fb_info);
633 mutex_unlock(&registration_lock);
634}
635EXPORT_SYMBOL(unregister_framebuffer);
636
637static void devm_unregister_framebuffer(void *data)
638{
639 struct fb_info *info = data;
640
641 unregister_framebuffer(info);
642}
643
644/**
645 * devm_register_framebuffer - resource-managed frame buffer device registration
646 * @dev: device the framebuffer belongs to
647 * @fb_info: frame buffer info structure
648 *
649 * Registers a frame buffer device @fb_info to device @dev.
650 *
651 * Returns negative errno on error, or zero for success.
652 *
653 */
654int
655devm_register_framebuffer(struct device *dev, struct fb_info *fb_info)
656{
657 int ret;
658
659 ret = register_framebuffer(fb_info);
660 if (ret)
661 return ret;
662
663 return devm_add_action_or_reset(dev, devm_unregister_framebuffer, fb_info);
664}
665EXPORT_SYMBOL(devm_register_framebuffer);
666
667/**
668 * fb_set_suspend - low level driver signals suspend
669 * @info: framebuffer affected
670 * @state: 0 = resuming, !=0 = suspending
671 *
672 * This is meant to be used by low level drivers to
673 * signal suspend/resume to the core & clients.
674 * It must be called with the console semaphore held
675 */
676void fb_set_suspend(struct fb_info *info, int state)
677{
678 WARN_CONSOLE_UNLOCKED();
679
680 if (state) {
681 fbcon_suspended(info);
682 info->state = FBINFO_STATE_SUSPENDED;
683 } else {
684 info->state = FBINFO_STATE_RUNNING;
685 fbcon_resumed(info);
686 }
687}
688EXPORT_SYMBOL(fb_set_suspend);
689
690/**
691 * fb_switch_outputs - framebuffer got the outputs from vga-switcheroo
692 * @info: framebuffer
693 */
694void fb_switch_outputs(struct fb_info *info)
695{
696 fbcon_remap_all(info);
697}
698EXPORT_SYMBOL(fb_switch_outputs);
699
700static int __init fbmem_init(void)
701{
702 int ret;
703
704 fb_class = class_create("graphics");
705 if (IS_ERR(fb_class)) {
706 ret = PTR_ERR(fb_class);
707 pr_err("Unable to create fb class; errno = %d\n", ret);
708 goto err_fb_class;
709 }
710
711 ret = fb_init_procfs();
712 if (ret)
713 goto err_class_destroy;
714
715 ret = fb_register_chrdev();
716 if (ret)
717 goto err_fb_cleanup_procfs;
718
719 fb_console_init();
720
721 return 0;
722
723err_fb_cleanup_procfs:
724 fb_cleanup_procfs();
725err_class_destroy:
726 class_destroy(fb_class);
727err_fb_class:
728 fb_class = NULL;
729 return ret;
730}
731
732#ifdef MODULE
733static void __exit fbmem_exit(void)
734{
735 fb_console_exit();
736 fb_unregister_chrdev();
737 fb_cleanup_procfs();
738 class_destroy(fb_class);
739}
740
741module_init(fbmem_init);
742module_exit(fbmem_exit);
743MODULE_LICENSE("GPL");
744MODULE_DESCRIPTION("Framebuffer base");
745#else
746subsys_initcall(fbmem_init);
747#endif
748
749int fb_new_modelist(struct fb_info *info)
750{
751 struct fb_var_screeninfo var = info->var;
752 struct list_head *pos, *n;
753 struct fb_modelist *modelist;
754 struct fb_videomode *m, mode;
755 int err;
756
757 list_for_each_safe(pos, n, &info->modelist) {
758 modelist = list_entry(pos, struct fb_modelist, list);
759 m = &modelist->mode;
760 fb_videomode_to_var(&var, m);
761 var.activate = FB_ACTIVATE_TEST;
762 err = fb_set_var(info, &var);
763 fb_var_to_videomode(&mode, &var);
764 if (err || !fb_mode_is_equal(m, &mode)) {
765 list_del(pos);
766 kfree(pos);
767 }
768 }
769
770 if (list_empty(&info->modelist))
771 return 1;
772
773 /*
774 * The new modelist may not contain the current mode (info->var), and
775 * fbcon_new_modelist() below only re-points consoles mapped to this
776 * framebuffer. Add the current mode here so info->var keeps a match
777 * even when fbcon is unbound.
778 */
779 if (!fb_match_mode(&info->var, &info->modelist)) {
780 fb_var_to_videomode(&mode, &info->var);
781 if (fb_add_videomode(&mode, &info->modelist))
782 return 1;
783 }
784
785 fbcon_new_modelist(info);
786
787 return 0;
788}
789
790bool fb_modesetting_disabled(const char *drvname)
791{
792 bool fwonly = video_firmware_drivers_only();
793
794 if (fwonly)
795 pr_warn("Driver %s not loading because of nomodeset parameter\n",
796 drvname);
797
798 return fwonly;
799}
800EXPORT_SYMBOL(fb_modesetting_disabled);
801
802MODULE_LICENSE("GPL");