1 /*
2 * linux/drivers/video/fbmem.c
3 *
4 * Copyright (C) 1994 Martin Schaller
5 *
6 * 2001 - Documented with DocBook
7 * - Brad Douglas <brad@neruo.com>
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
31 struct class *fb_class;
32
33 DEFINE_MUTEX(registration_lock);
34 struct fb_info *registered_fb[FB_MAX] __read_mostly;
35 int 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
get_fb_info(unsigned int idx)40 struct 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(®istration_lock);
48 fb_info = registered_fb[idx];
49 if (fb_info)
50 refcount_inc(&fb_info->count);
51 mutex_unlock(®istration_lock);
52
53 return fb_info;
54 }
55
put_fb_info(struct fb_info * fb_info)56 void 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
fb_get_color_depth(struct fb_var_screeninfo * var,struct fb_fix_screeninfo * fix)68 int 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 }
89 EXPORT_SYMBOL(fb_get_color_depth);
90
91 /*
92 * Data padding functions.
93 */
fb_pad_aligned_buffer(u8 * dst,u32 d_pitch,const u8 * src,u32 s_pitch,u32 height)94 void 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 }
98 EXPORT_SYMBOL(fb_pad_aligned_buffer);
99
fb_pad_unaligned_buffer(u8 * dst,u32 d_pitch,const u8 * src,u32 idx,u32 height,u32 shift_high,u32 shift_low,u32 mod)100 void 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 }
128 EXPORT_SYMBOL(fb_pad_unaligned_buffer);
129
130 /*
131 * we need to lock this section since fb_cursor
132 * may use fb_imageblit()
133 */
fb_get_buffer_offset(struct fb_info * info,struct fb_pixmap * buf,u32 size)134 char* 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 }
165 EXPORT_SYMBOL(fb_get_buffer_offset);
166
167 int
fb_pan_display(struct fb_info * info,struct fb_var_screeninfo * var)168 fb_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 }
203 EXPORT_SYMBOL(fb_pan_display);
204
fb_check_caps(struct fb_info * info,struct fb_var_screeninfo * var,u32 activate)205 static 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
fb_lcd_notify_mode_change(struct fb_info * info,struct fb_videomode * mode)225 static 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
231 int
fb_set_var(struct fb_info * info,struct fb_var_screeninfo * var)232 fb_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 }
350 EXPORT_SYMBOL(fb_set_var);
351
fb_set_var_from_user(struct fb_info * info,struct fb_var_screeninfo * var)352 int 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 }
363 EXPORT_SYMBOL(fb_set_var_from_user);
364
fb_lcd_notify_blank(struct fb_info * info)365 static 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
fb_ledtrig_backlight_notify_blank(struct fb_info * info)391 static 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
fb_blank(struct fb_info * info,int blank)399 int 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
422 err:
423 info->blank = old_blank;
424 return ret;
425 }
426 EXPORT_SYMBOL(fb_blank);
427
fb_blank_from_user(struct fb_info * info,int blank)428 int 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
fb_check_foreignness(struct fb_info * fi)438 static 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
do_register_framebuffer(struct fb_info * fb_info)463 static 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
unbind_console(struct fb_info * fb_info)541 static 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
unlink_framebuffer(struct fb_info * fb_info)551 static 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
do_unregister_framebuffer(struct fb_info * fb_info)564 static 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 */
599 int
register_framebuffer(struct fb_info * fb_info)600 register_framebuffer(struct fb_info *fb_info)
601 {
602 int ret;
603
604 mutex_lock(®istration_lock);
605 ret = do_register_framebuffer(fb_info);
606 mutex_unlock(®istration_lock);
607
608 return ret;
609 }
610 EXPORT_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 */
628 void
unregister_framebuffer(struct fb_info * fb_info)629 unregister_framebuffer(struct fb_info *fb_info)
630 {
631 mutex_lock(®istration_lock);
632 do_unregister_framebuffer(fb_info);
633 mutex_unlock(®istration_lock);
634 }
635 EXPORT_SYMBOL(unregister_framebuffer);
636
devm_unregister_framebuffer(void * data)637 static 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 */
654 int
devm_register_framebuffer(struct device * dev,struct fb_info * fb_info)655 devm_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 }
665 EXPORT_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 */
fb_set_suspend(struct fb_info * info,int state)676 void 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 }
688 EXPORT_SYMBOL(fb_set_suspend);
689
690 /**
691 * fb_switch_outputs - framebuffer got the outputs from vga-switcheroo
692 * @info: framebuffer
693 */
fb_switch_outputs(struct fb_info * info)694 void fb_switch_outputs(struct fb_info *info)
695 {
696 fbcon_remap_all(info);
697 }
698 EXPORT_SYMBOL(fb_switch_outputs);
699
fbmem_init(void)700 static 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
723 err_fb_cleanup_procfs:
724 fb_cleanup_procfs();
725 err_class_destroy:
726 class_destroy(fb_class);
727 err_fb_class:
728 fb_class = NULL;
729 return ret;
730 }
731
732 #ifdef MODULE
fbmem_exit(void)733 static 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
741 module_init(fbmem_init);
742 module_exit(fbmem_exit);
743 MODULE_LICENSE("GPL");
744 MODULE_DESCRIPTION("Framebuffer base");
745 #else
746 subsys_initcall(fbmem_init);
747 #endif
748
fb_new_modelist(struct fb_info * info)749 int 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
fb_modesetting_disabled(const char * drvname)790 bool 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 }
800 EXPORT_SYMBOL(fb_modesetting_disabled);
801
802 MODULE_LICENSE("GPL");
803