Merge tag 'drm-misc-fixes-2020-10-01' of git://anongit.freedesktop.org/drm/drm-misc...
[linux-2.6-microblaze.git] / drivers / video / fbdev / core / fbcon.c
1 /*
2  *  linux/drivers/video/fbcon.c -- Low level frame buffer based console driver
3  *
4  *      Copyright (C) 1995 Geert Uytterhoeven
5  *
6  *
7  *  This file is based on the original Amiga console driver (amicon.c):
8  *
9  *      Copyright (C) 1993 Hamish Macdonald
10  *                         Greg Harp
11  *      Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk]
12  *
13  *            with work by William Rucklidge (wjr@cs.cornell.edu)
14  *                         Geert Uytterhoeven
15  *                         Jes Sorensen (jds@kom.auc.dk)
16  *                         Martin Apel
17  *
18  *  and on the original Atari console driver (atacon.c):
19  *
20  *      Copyright (C) 1993 Bjoern Brauel
21  *                         Roman Hodek
22  *
23  *            with work by Guenther Kelleter
24  *                         Martin Schaller
25  *                         Andreas Schwab
26  *
27  *  Hardware cursor support added by Emmanuel Marty (core@ggi-project.org)
28  *  Smart redraw scrolling, arbitrary font width support, 512char font support
29  *  and software scrollback added by 
30  *                         Jakub Jelinek (jj@ultra.linux.cz)
31  *
32  *  Random hacking by Martin Mares <mj@ucw.cz>
33  *
34  *      2001 - Documented with DocBook
35  *      - Brad Douglas <brad@neruo.com>
36  *
37  *  The low level operations for the various display memory organizations are
38  *  now in separate source files.
39  *
40  *  Currently the following organizations are supported:
41  *
42  *    o afb                     Amiga bitplanes
43  *    o cfb{2,4,8,16,24,32}     Packed pixels
44  *    o ilbm                    Amiga interleaved bitplanes
45  *    o iplan2p[248]            Atari interleaved bitplanes
46  *    o mfb                     Monochrome
47  *    o vga                     VGA characters/attributes
48  *
49  *  To do:
50  *
51  *    - Implement 16 plane mode (iplan2p16)
52  *
53  *
54  *  This file is subject to the terms and conditions of the GNU General Public
55  *  License.  See the file COPYING in the main directory of this archive for
56  *  more details.
57  */
58
59 #undef FBCONDEBUG
60
61 #include <linux/module.h>
62 #include <linux/types.h>
63 #include <linux/fs.h>
64 #include <linux/kernel.h>
65 #include <linux/delay.h>        /* MSch: for IRQ probe */
66 #include <linux/console.h>
67 #include <linux/string.h>
68 #include <linux/kd.h>
69 #include <linux/slab.h>
70 #include <linux/fb.h>
71 #include <linux/fbcon.h>
72 #include <linux/vt_kern.h>
73 #include <linux/selection.h>
74 #include <linux/font.h>
75 #include <linux/smp.h>
76 #include <linux/init.h>
77 #include <linux/interrupt.h>
78 #include <linux/crc32.h> /* For counting font checksums */
79 #include <linux/uaccess.h>
80 #include <asm/fb.h>
81 #include <asm/irq.h>
82
83 #include "fbcon.h"
84
85 #ifdef FBCONDEBUG
86 #  define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
87 #else
88 #  define DPRINTK(fmt, args...)
89 #endif
90
91 /*
92  * FIXME: Locking
93  *
94  * - fbcon state itself is protected by the console_lock, and the code does a
95  *   pretty good job at making sure that lock is held everywhere it's needed.
96  *
97  * - access to the registered_fb array is entirely unprotected. This should use
98  *   proper object lifetime handling, i.e. get/put_fb_info. This also means
99  *   switching from indices to proper pointers for fb_info everywhere.
100  *
101  * - fbcon doesn't bother with fb_lock/unlock at all. This is buggy, since it
102  *   means concurrent access to the same fbdev from both fbcon and userspace
103  *   will blow up. To fix this all fbcon calls from fbmem.c need to be moved out
104  *   of fb_lock/unlock protected sections, since otherwise we'll recurse and
105  *   deadlock eventually. Aside: Due to these deadlock issues the fbdev code in
106  *   fbmem.c cannot use locking asserts, and there's lots of callers which get
107  *   the rules wrong, e.g. fbsysfs.c entirely missed fb_lock/unlock calls too.
108  */
109
110 enum {
111         FBCON_LOGO_CANSHOW      = -1,   /* the logo can be shown */
112         FBCON_LOGO_DRAW         = -2,   /* draw the logo to a console */
113         FBCON_LOGO_DONTSHOW     = -3    /* do not show the logo */
114 };
115
116 static struct fbcon_display fb_display[MAX_NR_CONSOLES];
117
118 static signed char con2fb_map[MAX_NR_CONSOLES];
119 static signed char con2fb_map_boot[MAX_NR_CONSOLES];
120
121 static int logo_lines;
122 /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO
123    enums.  */
124 static int logo_shown = FBCON_LOGO_CANSHOW;
125 /* console mappings */
126 static int first_fb_vc;
127 static int last_fb_vc = MAX_NR_CONSOLES - 1;
128 static int fbcon_is_default = 1; 
129 static int primary_device = -1;
130 static int fbcon_has_console_bind;
131
132 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
133 static int map_override;
134
135 static inline void fbcon_map_override(void)
136 {
137         map_override = 1;
138 }
139 #else
140 static inline void fbcon_map_override(void)
141 {
142 }
143 #endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */
144
145 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
146 static bool deferred_takeover = true;
147 #else
148 #define deferred_takeover false
149 #endif
150
151 /* font data */
152 static char fontname[40];
153
154 /* current fb_info */
155 static int info_idx = -1;
156
157 /* console rotation */
158 static int initial_rotation = -1;
159 static int fbcon_has_sysfs;
160 static int margin_color;
161
162 static const struct consw fb_con;
163
164 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
165
166 static int fbcon_set_origin(struct vc_data *);
167
168 static int fbcon_cursor_noblink;
169
170 #define divides(a, b)   ((!(a) || (b)%(a)) ? 0 : 1)
171
172 /*
173  *  Interface used by the world
174  */
175
176 static const char *fbcon_startup(void);
177 static void fbcon_init(struct vc_data *vc, int init);
178 static void fbcon_deinit(struct vc_data *vc);
179 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
180                         int width);
181 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos);
182 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
183                         int count, int ypos, int xpos);
184 static void fbcon_clear_margins(struct vc_data *vc, int bottom_only);
185 static void fbcon_cursor(struct vc_data *vc, int mode);
186 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
187                         int height, int width);
188 static int fbcon_switch(struct vc_data *vc);
189 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch);
190 static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table);
191
192 /*
193  *  Internal routines
194  */
195 static __inline__ void ywrap_up(struct vc_data *vc, int count);
196 static __inline__ void ywrap_down(struct vc_data *vc, int count);
197 static __inline__ void ypan_up(struct vc_data *vc, int count);
198 static __inline__ void ypan_down(struct vc_data *vc, int count);
199 static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx,
200                             int dy, int dx, int height, int width, u_int y_break);
201 static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
202                            int unit);
203 static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
204                               int line, int count, int dy);
205 static void fbcon_modechanged(struct fb_info *info);
206 static void fbcon_set_all_vcs(struct fb_info *info);
207 static void fbcon_start(void);
208 static void fbcon_exit(void);
209 static struct device *fbcon_device;
210
211 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
212 static inline void fbcon_set_rotation(struct fb_info *info)
213 {
214         struct fbcon_ops *ops = info->fbcon_par;
215
216         if (!(info->flags & FBINFO_MISC_TILEBLITTING) &&
217             ops->p->con_rotate < 4)
218                 ops->rotate = ops->p->con_rotate;
219         else
220                 ops->rotate = 0;
221 }
222
223 static void fbcon_rotate(struct fb_info *info, u32 rotate)
224 {
225         struct fbcon_ops *ops= info->fbcon_par;
226         struct fb_info *fb_info;
227
228         if (!ops || ops->currcon == -1)
229                 return;
230
231         fb_info = registered_fb[con2fb_map[ops->currcon]];
232
233         if (info == fb_info) {
234                 struct fbcon_display *p = &fb_display[ops->currcon];
235
236                 if (rotate < 4)
237                         p->con_rotate = rotate;
238                 else
239                         p->con_rotate = 0;
240
241                 fbcon_modechanged(info);
242         }
243 }
244
245 static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
246 {
247         struct fbcon_ops *ops = info->fbcon_par;
248         struct vc_data *vc;
249         struct fbcon_display *p;
250         int i;
251
252         if (!ops || ops->currcon < 0 || rotate > 3)
253                 return;
254
255         for (i = first_fb_vc; i <= last_fb_vc; i++) {
256                 vc = vc_cons[i].d;
257                 if (!vc || vc->vc_mode != KD_TEXT ||
258                     registered_fb[con2fb_map[i]] != info)
259                         continue;
260
261                 p = &fb_display[vc->vc_num];
262                 p->con_rotate = rotate;
263         }
264
265         fbcon_set_all_vcs(info);
266 }
267 #else
268 static inline void fbcon_set_rotation(struct fb_info *info)
269 {
270         struct fbcon_ops *ops = info->fbcon_par;
271
272         ops->rotate = FB_ROTATE_UR;
273 }
274
275 static void fbcon_rotate(struct fb_info *info, u32 rotate)
276 {
277         return;
278 }
279
280 static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
281 {
282         return;
283 }
284 #endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */
285
286 static int fbcon_get_rotate(struct fb_info *info)
287 {
288         struct fbcon_ops *ops = info->fbcon_par;
289
290         return (ops) ? ops->rotate : 0;
291 }
292
293 static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
294 {
295         struct fbcon_ops *ops = info->fbcon_par;
296
297         return (info->state != FBINFO_STATE_RUNNING ||
298                 vc->vc_mode != KD_TEXT || ops->graphics);
299 }
300
301 static int get_color(struct vc_data *vc, struct fb_info *info,
302               u16 c, int is_fg)
303 {
304         int depth = fb_get_color_depth(&info->var, &info->fix);
305         int color = 0;
306
307         if (console_blanked) {
308                 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
309
310                 c = vc->vc_video_erase_char & charmask;
311         }
312
313         if (depth != 1)
314                 color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c)
315                         : attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c);
316
317         switch (depth) {
318         case 1:
319         {
320                 int col = mono_col(info);
321                 /* 0 or 1 */
322                 int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0;
323                 int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col;
324
325                 if (console_blanked)
326                         fg = bg;
327
328                 color = (is_fg) ? fg : bg;
329                 break;
330         }
331         case 2:
332                 /*
333                  * Scale down 16-colors to 4 colors. Default 4-color palette
334                  * is grayscale. However, simply dividing the values by 4
335                  * will not work, as colors 1, 2 and 3 will be scaled-down
336                  * to zero rendering them invisible.  So empirically convert
337                  * colors to a sane 4-level grayscale.
338                  */
339                 switch (color) {
340                 case 0:
341                         color = 0; /* black */
342                         break;
343                 case 1 ... 6:
344                         color = 2; /* white */
345                         break;
346                 case 7 ... 8:
347                         color = 1; /* gray */
348                         break;
349                 default:
350                         color = 3; /* intense white */
351                         break;
352                 }
353                 break;
354         case 3:
355                 /*
356                  * Last 8 entries of default 16-color palette is a more intense
357                  * version of the first 8 (i.e., same chrominance, different
358                  * luminance).
359                  */
360                 color &= 7;
361                 break;
362         }
363
364
365         return color;
366 }
367
368 static void fb_flashcursor(struct work_struct *work)
369 {
370         struct fb_info *info = container_of(work, struct fb_info, queue);
371         struct fbcon_ops *ops = info->fbcon_par;
372         struct vc_data *vc = NULL;
373         int c;
374         int mode;
375         int ret;
376
377         /* FIXME: we should sort out the unbind locking instead */
378         /* instead we just fail to flash the cursor if we can't get
379          * the lock instead of blocking fbcon deinit */
380         ret = console_trylock();
381         if (ret == 0)
382                 return;
383
384         if (ops && ops->currcon != -1)
385                 vc = vc_cons[ops->currcon].d;
386
387         if (!vc || !con_is_visible(vc) ||
388             registered_fb[con2fb_map[vc->vc_num]] != info ||
389             vc->vc_deccm != 1) {
390                 console_unlock();
391                 return;
392         }
393
394         c = scr_readw((u16 *) vc->vc_pos);
395         mode = (!ops->cursor_flash || ops->cursor_state.enable) ?
396                 CM_ERASE : CM_DRAW;
397         ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
398                     get_color(vc, info, c, 0));
399         console_unlock();
400 }
401
402 static void cursor_timer_handler(struct timer_list *t)
403 {
404         struct fbcon_ops *ops = from_timer(ops, t, cursor_timer);
405         struct fb_info *info = ops->info;
406
407         queue_work(system_power_efficient_wq, &info->queue);
408         mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies);
409 }
410
411 static void fbcon_add_cursor_timer(struct fb_info *info)
412 {
413         struct fbcon_ops *ops = info->fbcon_par;
414
415         if ((!info->queue.func || info->queue.func == fb_flashcursor) &&
416             !(ops->flags & FBCON_FLAGS_CURSOR_TIMER) &&
417             !fbcon_cursor_noblink) {
418                 if (!info->queue.func)
419                         INIT_WORK(&info->queue, fb_flashcursor);
420
421                 timer_setup(&ops->cursor_timer, cursor_timer_handler, 0);
422                 mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies);
423                 ops->flags |= FBCON_FLAGS_CURSOR_TIMER;
424         }
425 }
426
427 static void fbcon_del_cursor_timer(struct fb_info *info)
428 {
429         struct fbcon_ops *ops = info->fbcon_par;
430
431         if (info->queue.func == fb_flashcursor &&
432             ops->flags & FBCON_FLAGS_CURSOR_TIMER) {
433                 del_timer_sync(&ops->cursor_timer);
434                 ops->flags &= ~FBCON_FLAGS_CURSOR_TIMER;
435         }
436 }
437
438 #ifndef MODULE
439 static int __init fb_console_setup(char *this_opt)
440 {
441         char *options;
442         int i, j;
443
444         if (!this_opt || !*this_opt)
445                 return 1;
446
447         while ((options = strsep(&this_opt, ",")) != NULL) {
448                 if (!strncmp(options, "font:", 5)) {
449                         strlcpy(fontname, options + 5, sizeof(fontname));
450                         continue;
451                 }
452                 
453                 if (!strncmp(options, "scrollback:", 11)) {
454                         pr_warn("Ignoring scrollback size option\n");
455                         continue;
456                 }
457                 
458                 if (!strncmp(options, "map:", 4)) {
459                         options += 4;
460                         if (*options) {
461                                 for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
462                                         if (!options[j])
463                                                 j = 0;
464                                         con2fb_map_boot[i] =
465                                                 (options[j++]-'0') % FB_MAX;
466                                 }
467
468                                 fbcon_map_override();
469                         }
470                         continue;
471                 }
472
473                 if (!strncmp(options, "vc:", 3)) {
474                         options += 3;
475                         if (*options)
476                                 first_fb_vc = simple_strtoul(options, &options, 10) - 1;
477                         if (first_fb_vc < 0)
478                                 first_fb_vc = 0;
479                         if (*options++ == '-')
480                                 last_fb_vc = simple_strtoul(options, &options, 10) - 1;
481                         fbcon_is_default = 0; 
482                         continue;
483                 }
484
485                 if (!strncmp(options, "rotate:", 7)) {
486                         options += 7;
487                         if (*options)
488                                 initial_rotation = simple_strtoul(options, &options, 0);
489                         if (initial_rotation > 3)
490                                 initial_rotation = 0;
491                         continue;
492                 }
493
494                 if (!strncmp(options, "margin:", 7)) {
495                         options += 7;
496                         if (*options)
497                                 margin_color = simple_strtoul(options, &options, 0);
498                         continue;
499                 }
500 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
501                 if (!strcmp(options, "nodefer")) {
502                         deferred_takeover = false;
503                         continue;
504                 }
505 #endif
506
507                 if (!strncmp(options, "logo-pos:", 9)) {
508                         options += 9;
509                         if (!strcmp(options, "center"))
510                                 fb_center_logo = true;
511                         continue;
512                 }
513
514                 if (!strncmp(options, "logo-count:", 11)) {
515                         options += 11;
516                         if (*options)
517                                 fb_logo_count = simple_strtol(options, &options, 0);
518                         continue;
519                 }
520         }
521         return 1;
522 }
523
524 __setup("fbcon=", fb_console_setup);
525 #endif
526
527 static int search_fb_in_map(int idx)
528 {
529         int i, retval = 0;
530
531         for (i = first_fb_vc; i <= last_fb_vc; i++) {
532                 if (con2fb_map[i] == idx)
533                         retval = 1;
534         }
535         return retval;
536 }
537
538 static int search_for_mapped_con(void)
539 {
540         int i, retval = 0;
541
542         for (i = first_fb_vc; i <= last_fb_vc; i++) {
543                 if (con2fb_map[i] != -1)
544                         retval = 1;
545         }
546         return retval;
547 }
548
549 static int do_fbcon_takeover(int show_logo)
550 {
551         int err, i;
552
553         if (!num_registered_fb)
554                 return -ENODEV;
555
556         if (!show_logo)
557                 logo_shown = FBCON_LOGO_DONTSHOW;
558
559         for (i = first_fb_vc; i <= last_fb_vc; i++)
560                 con2fb_map[i] = info_idx;
561
562         err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc,
563                                 fbcon_is_default);
564
565         if (err) {
566                 for (i = first_fb_vc; i <= last_fb_vc; i++)
567                         con2fb_map[i] = -1;
568                 info_idx = -1;
569         } else {
570                 fbcon_has_console_bind = 1;
571         }
572
573         return err;
574 }
575
576 #ifdef MODULE
577 static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
578                                int cols, int rows, int new_cols, int new_rows)
579 {
580         logo_shown = FBCON_LOGO_DONTSHOW;
581 }
582 #else
583 static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
584                                int cols, int rows, int new_cols, int new_rows)
585 {
586         /* Need to make room for the logo */
587         struct fbcon_ops *ops = info->fbcon_par;
588         int cnt, erase = vc->vc_video_erase_char, step;
589         unsigned short *save = NULL, *r, *q;
590         int logo_height;
591
592         if (info->fbops->owner) {
593                 logo_shown = FBCON_LOGO_DONTSHOW;
594                 return;
595         }
596
597         /*
598          * remove underline attribute from erase character
599          * if black and white framebuffer.
600          */
601         if (fb_get_color_depth(&info->var, &info->fix) == 1)
602                 erase &= ~0x400;
603         logo_height = fb_prepare_logo(info, ops->rotate);
604         logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
605         q = (unsigned short *) (vc->vc_origin +
606                                 vc->vc_size_row * rows);
607         step = logo_lines * cols;
608         for (r = q - logo_lines * cols; r < q; r++)
609                 if (scr_readw(r) != vc->vc_video_erase_char)
610                         break;
611         if (r != q && new_rows >= rows + logo_lines) {
612                 save = kmalloc(array3_size(logo_lines, new_cols, 2),
613                                GFP_KERNEL);
614                 if (save) {
615                         int i = cols < new_cols ? cols : new_cols;
616                         scr_memsetw(save, erase, array3_size(logo_lines, new_cols, 2));
617                         r = q - step;
618                         for (cnt = 0; cnt < logo_lines; cnt++, r += i)
619                                 scr_memcpyw(save + cnt * new_cols, r, 2 * i);
620                         r = q;
621                 }
622         }
623         if (r == q) {
624                 /* We can scroll screen down */
625                 r = q - step - cols;
626                 for (cnt = rows - logo_lines; cnt > 0; cnt--) {
627                         scr_memcpyw(r + step, r, vc->vc_size_row);
628                         r -= cols;
629                 }
630                 if (!save) {
631                         int lines;
632                         if (vc->state.y + logo_lines >= rows)
633                                 lines = rows - vc->state.y - 1;
634                         else
635                                 lines = logo_lines;
636                         vc->state.y += lines;
637                         vc->vc_pos += lines * vc->vc_size_row;
638                 }
639         }
640         scr_memsetw((unsigned short *) vc->vc_origin,
641                     erase,
642                     vc->vc_size_row * logo_lines);
643
644         if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
645                 fbcon_clear_margins(vc, 0);
646                 update_screen(vc);
647         }
648
649         if (save) {
650                 q = (unsigned short *) (vc->vc_origin +
651                                         vc->vc_size_row *
652                                         rows);
653                 scr_memcpyw(q, save, array3_size(logo_lines, new_cols, 2));
654                 vc->state.y += logo_lines;
655                 vc->vc_pos += logo_lines * vc->vc_size_row;
656                 kfree(save);
657         }
658
659         if (logo_shown == FBCON_LOGO_DONTSHOW)
660                 return;
661
662         if (logo_lines > vc->vc_bottom) {
663                 logo_shown = FBCON_LOGO_CANSHOW;
664                 printk(KERN_INFO
665                        "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
666         } else {
667                 logo_shown = FBCON_LOGO_DRAW;
668                 vc->vc_top = logo_lines;
669         }
670 }
671 #endif /* MODULE */
672
673 #ifdef CONFIG_FB_TILEBLITTING
674 static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
675 {
676         struct fbcon_ops *ops = info->fbcon_par;
677
678         ops->p = &fb_display[vc->vc_num];
679
680         if ((info->flags & FBINFO_MISC_TILEBLITTING))
681                 fbcon_set_tileops(vc, info);
682         else {
683                 fbcon_set_rotation(info);
684                 fbcon_set_bitops(ops);
685         }
686 }
687
688 static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
689 {
690         int err = 0;
691
692         if (info->flags & FBINFO_MISC_TILEBLITTING &&
693             info->tileops->fb_get_tilemax(info) < charcount)
694                 err = 1;
695
696         return err;
697 }
698 #else
699 static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
700 {
701         struct fbcon_ops *ops = info->fbcon_par;
702
703         info->flags &= ~FBINFO_MISC_TILEBLITTING;
704         ops->p = &fb_display[vc->vc_num];
705         fbcon_set_rotation(info);
706         fbcon_set_bitops(ops);
707 }
708
709 static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
710 {
711         return 0;
712 }
713
714 #endif /* CONFIG_MISC_TILEBLITTING */
715
716
717 static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info,
718                                   int unit, int oldidx)
719 {
720         struct fbcon_ops *ops = NULL;
721         int err = 0;
722
723         if (!try_module_get(info->fbops->owner))
724                 err = -ENODEV;
725
726         if (!err && info->fbops->fb_open &&
727             info->fbops->fb_open(info, 0))
728                 err = -ENODEV;
729
730         if (!err) {
731                 ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
732                 if (!ops)
733                         err = -ENOMEM;
734         }
735
736         if (!err) {
737                 ops->cur_blink_jiffies = HZ / 5;
738                 ops->info = info;
739                 info->fbcon_par = ops;
740
741                 if (vc)
742                         set_blitting_type(vc, info);
743         }
744
745         if (err) {
746                 con2fb_map[unit] = oldidx;
747                 module_put(info->fbops->owner);
748         }
749
750         return err;
751 }
752
753 static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,
754                                   struct fb_info *newinfo, int unit,
755                                   int oldidx, int found)
756 {
757         struct fbcon_ops *ops = oldinfo->fbcon_par;
758         int err = 0, ret;
759
760         if (oldinfo->fbops->fb_release &&
761             oldinfo->fbops->fb_release(oldinfo, 0)) {
762                 con2fb_map[unit] = oldidx;
763                 if (!found && newinfo->fbops->fb_release)
764                         newinfo->fbops->fb_release(newinfo, 0);
765                 if (!found)
766                         module_put(newinfo->fbops->owner);
767                 err = -ENODEV;
768         }
769
770         if (!err) {
771                 fbcon_del_cursor_timer(oldinfo);
772                 kfree(ops->cursor_state.mask);
773                 kfree(ops->cursor_data);
774                 kfree(ops->cursor_src);
775                 kfree(ops->fontbuffer);
776                 kfree(oldinfo->fbcon_par);
777                 oldinfo->fbcon_par = NULL;
778                 module_put(oldinfo->fbops->owner);
779                 /*
780                   If oldinfo and newinfo are driving the same hardware,
781                   the fb_release() method of oldinfo may attempt to
782                   restore the hardware state.  This will leave the
783                   newinfo in an undefined state. Thus, a call to
784                   fb_set_par() may be needed for the newinfo.
785                 */
786                 if (newinfo && newinfo->fbops->fb_set_par) {
787                         ret = newinfo->fbops->fb_set_par(newinfo);
788
789                         if (ret)
790                                 printk(KERN_ERR "con2fb_release_oldinfo: "
791                                         "detected unhandled fb_set_par error, "
792                                         "error code %d\n", ret);
793                 }
794         }
795
796         return err;
797 }
798
799 static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,
800                                 int unit, int show_logo)
801 {
802         struct fbcon_ops *ops = info->fbcon_par;
803         int ret;
804
805         ops->currcon = fg_console;
806
807         if (info->fbops->fb_set_par && !(ops->flags & FBCON_FLAGS_INIT)) {
808                 ret = info->fbops->fb_set_par(info);
809
810                 if (ret)
811                         printk(KERN_ERR "con2fb_init_display: detected "
812                                 "unhandled fb_set_par error, "
813                                 "error code %d\n", ret);
814         }
815
816         ops->flags |= FBCON_FLAGS_INIT;
817         ops->graphics = 0;
818         fbcon_set_disp(info, &info->var, unit);
819
820         if (show_logo) {
821                 struct vc_data *fg_vc = vc_cons[fg_console].d;
822                 struct fb_info *fg_info =
823                         registered_fb[con2fb_map[fg_console]];
824
825                 fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols,
826                                    fg_vc->vc_rows, fg_vc->vc_cols,
827                                    fg_vc->vc_rows);
828         }
829
830         update_screen(vc_cons[fg_console].d);
831 }
832
833 /**
834  *      set_con2fb_map - map console to frame buffer device
835  *      @unit: virtual console number to map
836  *      @newidx: frame buffer index to map virtual console to
837  *      @user: user request
838  *
839  *      Maps a virtual console @unit to a frame buffer device
840  *      @newidx.
841  *
842  *      This should be called with the console lock held.
843  */
844 static int set_con2fb_map(int unit, int newidx, int user)
845 {
846         struct vc_data *vc = vc_cons[unit].d;
847         int oldidx = con2fb_map[unit];
848         struct fb_info *info = registered_fb[newidx];
849         struct fb_info *oldinfo = NULL;
850         int found, err = 0;
851
852         WARN_CONSOLE_UNLOCKED();
853
854         if (oldidx == newidx)
855                 return 0;
856
857         if (!info)
858                 return -EINVAL;
859
860         if (!search_for_mapped_con() || !con_is_bound(&fb_con)) {
861                 info_idx = newidx;
862                 return do_fbcon_takeover(0);
863         }
864
865         if (oldidx != -1)
866                 oldinfo = registered_fb[oldidx];
867
868         found = search_fb_in_map(newidx);
869
870         con2fb_map[unit] = newidx;
871         if (!err && !found)
872                 err = con2fb_acquire_newinfo(vc, info, unit, oldidx);
873
874         /*
875          * If old fb is not mapped to any of the consoles,
876          * fbcon should release it.
877          */
878         if (!err && oldinfo && !search_fb_in_map(oldidx))
879                 err = con2fb_release_oldinfo(vc, oldinfo, info, unit, oldidx,
880                                              found);
881
882         if (!err) {
883                 int show_logo = (fg_console == 0 && !user &&
884                                  logo_shown != FBCON_LOGO_DONTSHOW);
885
886                 if (!found)
887                         fbcon_add_cursor_timer(info);
888                 con2fb_map_boot[unit] = newidx;
889                 con2fb_init_display(vc, info, unit, show_logo);
890         }
891
892         if (!search_fb_in_map(info_idx))
893                 info_idx = newidx;
894
895         return err;
896 }
897
898 /*
899  *  Low Level Operations
900  */
901 /* NOTE: fbcon cannot be __init: it may be called from do_take_over_console later */
902 static int var_to_display(struct fbcon_display *disp,
903                           struct fb_var_screeninfo *var,
904                           struct fb_info *info)
905 {
906         disp->xres_virtual = var->xres_virtual;
907         disp->yres_virtual = var->yres_virtual;
908         disp->bits_per_pixel = var->bits_per_pixel;
909         disp->grayscale = var->grayscale;
910         disp->nonstd = var->nonstd;
911         disp->accel_flags = var->accel_flags;
912         disp->height = var->height;
913         disp->width = var->width;
914         disp->red = var->red;
915         disp->green = var->green;
916         disp->blue = var->blue;
917         disp->transp = var->transp;
918         disp->rotate = var->rotate;
919         disp->mode = fb_match_mode(var, &info->modelist);
920         if (disp->mode == NULL)
921                 /* This should not happen */
922                 return -EINVAL;
923         return 0;
924 }
925
926 static void display_to_var(struct fb_var_screeninfo *var,
927                            struct fbcon_display *disp)
928 {
929         fb_videomode_to_var(var, disp->mode);
930         var->xres_virtual = disp->xres_virtual;
931         var->yres_virtual = disp->yres_virtual;
932         var->bits_per_pixel = disp->bits_per_pixel;
933         var->grayscale = disp->grayscale;
934         var->nonstd = disp->nonstd;
935         var->accel_flags = disp->accel_flags;
936         var->height = disp->height;
937         var->width = disp->width;
938         var->red = disp->red;
939         var->green = disp->green;
940         var->blue = disp->blue;
941         var->transp = disp->transp;
942         var->rotate = disp->rotate;
943 }
944
945 static const char *fbcon_startup(void)
946 {
947         const char *display_desc = "frame buffer device";
948         struct fbcon_display *p = &fb_display[fg_console];
949         struct vc_data *vc = vc_cons[fg_console].d;
950         const struct font_desc *font = NULL;
951         struct module *owner;
952         struct fb_info *info = NULL;
953         struct fbcon_ops *ops;
954         int rows, cols;
955
956         /*
957          *  If num_registered_fb is zero, this is a call for the dummy part.
958          *  The frame buffer devices weren't initialized yet.
959          */
960         if (!num_registered_fb || info_idx == -1)
961                 return display_desc;
962         /*
963          * Instead of blindly using registered_fb[0], we use info_idx, set by
964          * fb_console_init();
965          */
966         info = registered_fb[info_idx];
967         if (!info)
968                 return NULL;
969         
970         owner = info->fbops->owner;
971         if (!try_module_get(owner))
972                 return NULL;
973         if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
974                 module_put(owner);
975                 return NULL;
976         }
977
978         ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
979         if (!ops) {
980                 module_put(owner);
981                 return NULL;
982         }
983
984         ops->currcon = -1;
985         ops->graphics = 1;
986         ops->cur_rotate = -1;
987         ops->cur_blink_jiffies = HZ / 5;
988         ops->info = info;
989         info->fbcon_par = ops;
990
991         p->con_rotate = initial_rotation;
992         if (p->con_rotate == -1)
993                 p->con_rotate = info->fbcon_rotate_hint;
994         if (p->con_rotate == -1)
995                 p->con_rotate = FB_ROTATE_UR;
996
997         set_blitting_type(vc, info);
998
999         /* Setup default font */
1000         if (!p->fontdata && !vc->vc_font.data) {
1001                 if (!fontname[0] || !(font = find_font(fontname)))
1002                         font = get_default_font(info->var.xres,
1003                                                 info->var.yres,
1004                                                 info->pixmap.blit_x,
1005                                                 info->pixmap.blit_y);
1006                 vc->vc_font.width = font->width;
1007                 vc->vc_font.height = font->height;
1008                 vc->vc_font.data = (void *)(p->fontdata = font->data);
1009                 vc->vc_font.charcount = 256; /* FIXME  Need to support more fonts */
1010         } else {
1011                 p->fontdata = vc->vc_font.data;
1012         }
1013
1014         cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1015         rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1016         cols /= vc->vc_font.width;
1017         rows /= vc->vc_font.height;
1018         vc_resize(vc, cols, rows);
1019
1020         DPRINTK("mode:   %s\n", info->fix.id);
1021         DPRINTK("visual: %d\n", info->fix.visual);
1022         DPRINTK("res:    %dx%d-%d\n", info->var.xres,
1023                 info->var.yres,
1024                 info->var.bits_per_pixel);
1025
1026         fbcon_add_cursor_timer(info);
1027         return display_desc;
1028 }
1029
1030 static void fbcon_init(struct vc_data *vc, int init)
1031 {
1032         struct fb_info *info;
1033         struct fbcon_ops *ops;
1034         struct vc_data **default_mode = vc->vc_display_fg;
1035         struct vc_data *svc = *default_mode;
1036         struct fbcon_display *t, *p = &fb_display[vc->vc_num];
1037         int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;
1038         int cap, ret;
1039
1040         if (WARN_ON(info_idx == -1))
1041             return;
1042
1043         if (con2fb_map[vc->vc_num] == -1)
1044                 con2fb_map[vc->vc_num] = info_idx;
1045
1046         info = registered_fb[con2fb_map[vc->vc_num]];
1047         cap = info->flags;
1048
1049         if (logo_shown < 0 && console_loglevel <= CONSOLE_LOGLEVEL_QUIET)
1050                 logo_shown = FBCON_LOGO_DONTSHOW;
1051
1052         if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
1053             (info->fix.type == FB_TYPE_TEXT))
1054                 logo = 0;
1055
1056         if (var_to_display(p, &info->var, info))
1057                 return;
1058
1059         if (!info->fbcon_par)
1060                 con2fb_acquire_newinfo(vc, info, vc->vc_num, -1);
1061
1062         /* If we are not the first console on this
1063            fb, copy the font from that console */
1064         t = &fb_display[fg_console];
1065         if (!p->fontdata) {
1066                 if (t->fontdata) {
1067                         struct vc_data *fvc = vc_cons[fg_console].d;
1068
1069                         vc->vc_font.data = (void *)(p->fontdata =
1070                                                     fvc->vc_font.data);
1071                         vc->vc_font.width = fvc->vc_font.width;
1072                         vc->vc_font.height = fvc->vc_font.height;
1073                         p->userfont = t->userfont;
1074
1075                         if (p->userfont)
1076                                 REFCOUNT(p->fontdata)++;
1077                 } else {
1078                         const struct font_desc *font = NULL;
1079
1080                         if (!fontname[0] || !(font = find_font(fontname)))
1081                                 font = get_default_font(info->var.xres,
1082                                                         info->var.yres,
1083                                                         info->pixmap.blit_x,
1084                                                         info->pixmap.blit_y);
1085                         vc->vc_font.width = font->width;
1086                         vc->vc_font.height = font->height;
1087                         vc->vc_font.data = (void *)(p->fontdata = font->data);
1088                         vc->vc_font.charcount = 256; /* FIXME  Need to
1089                                                         support more fonts */
1090                 }
1091         }
1092
1093         if (p->userfont)
1094                 charcnt = FNTCHARCNT(p->fontdata);
1095
1096         vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1097         vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1098         if (charcnt == 256) {
1099                 vc->vc_hi_font_mask = 0;
1100         } else {
1101                 vc->vc_hi_font_mask = 0x100;
1102                 if (vc->vc_can_do_color)
1103                         vc->vc_complement_mask <<= 1;
1104         }
1105
1106         if (!*svc->vc_uni_pagedir_loc)
1107                 con_set_default_unimap(svc);
1108         if (!*vc->vc_uni_pagedir_loc)
1109                 con_copy_unimap(vc, svc);
1110
1111         ops = info->fbcon_par;
1112         ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
1113
1114         p->con_rotate = initial_rotation;
1115         if (p->con_rotate == -1)
1116                 p->con_rotate = info->fbcon_rotate_hint;
1117         if (p->con_rotate == -1)
1118                 p->con_rotate = FB_ROTATE_UR;
1119
1120         set_blitting_type(vc, info);
1121
1122         cols = vc->vc_cols;
1123         rows = vc->vc_rows;
1124         new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1125         new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1126         new_cols /= vc->vc_font.width;
1127         new_rows /= vc->vc_font.height;
1128
1129         /*
1130          * We must always set the mode. The mode of the previous console
1131          * driver could be in the same resolution but we are using different
1132          * hardware so we have to initialize the hardware.
1133          *
1134          * We need to do it in fbcon_init() to prevent screen corruption.
1135          */
1136         if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
1137                 if (info->fbops->fb_set_par &&
1138                     !(ops->flags & FBCON_FLAGS_INIT)) {
1139                         ret = info->fbops->fb_set_par(info);
1140
1141                         if (ret)
1142                                 printk(KERN_ERR "fbcon_init: detected "
1143                                         "unhandled fb_set_par error, "
1144                                         "error code %d\n", ret);
1145                 }
1146
1147                 ops->flags |= FBCON_FLAGS_INIT;
1148         }
1149
1150         ops->graphics = 0;
1151
1152         if ((cap & FBINFO_HWACCEL_COPYAREA) &&
1153             !(cap & FBINFO_HWACCEL_DISABLED))
1154                 p->scrollmode = SCROLL_MOVE;
1155         else /* default to something safe */
1156                 p->scrollmode = SCROLL_REDRAW;
1157
1158         /*
1159          *  ++guenther: console.c:vc_allocate() relies on initializing
1160          *  vc_{cols,rows}, but we must not set those if we are only
1161          *  resizing the console.
1162          */
1163         if (init) {
1164                 vc->vc_cols = new_cols;
1165                 vc->vc_rows = new_rows;
1166         } else
1167                 vc_resize(vc, new_cols, new_rows);
1168
1169         if (logo)
1170                 fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
1171
1172         if (ops->rotate_font && ops->rotate_font(info, vc)) {
1173                 ops->rotate = FB_ROTATE_UR;
1174                 set_blitting_type(vc, info);
1175         }
1176
1177         ops->p = &fb_display[fg_console];
1178 }
1179
1180 static void fbcon_free_font(struct fbcon_display *p, bool freefont)
1181 {
1182         if (freefont && p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
1183                 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
1184         p->fontdata = NULL;
1185         p->userfont = 0;
1186 }
1187
1188 static void set_vc_hi_font(struct vc_data *vc, bool set);
1189
1190 static void fbcon_deinit(struct vc_data *vc)
1191 {
1192         struct fbcon_display *p = &fb_display[vc->vc_num];
1193         struct fb_info *info;
1194         struct fbcon_ops *ops;
1195         int idx;
1196         bool free_font = true;
1197
1198         idx = con2fb_map[vc->vc_num];
1199
1200         if (idx == -1)
1201                 goto finished;
1202
1203         info = registered_fb[idx];
1204
1205         if (!info)
1206                 goto finished;
1207
1208         if (info->flags & FBINFO_MISC_FIRMWARE)
1209                 free_font = false;
1210         ops = info->fbcon_par;
1211
1212         if (!ops)
1213                 goto finished;
1214
1215         if (con_is_visible(vc))
1216                 fbcon_del_cursor_timer(info);
1217
1218         ops->flags &= ~FBCON_FLAGS_INIT;
1219 finished:
1220
1221         fbcon_free_font(p, free_font);
1222         if (free_font)
1223                 vc->vc_font.data = NULL;
1224
1225         if (vc->vc_hi_font_mask && vc->vc_screenbuf)
1226                 set_vc_hi_font(vc, false);
1227
1228         if (!con_is_bound(&fb_con))
1229                 fbcon_exit();
1230
1231         if (vc->vc_num == logo_shown)
1232                 logo_shown = FBCON_LOGO_CANSHOW;
1233
1234         return;
1235 }
1236
1237 /* ====================================================================== */
1238
1239 /*  fbcon_XXX routines - interface used by the world
1240  *
1241  *  This system is now divided into two levels because of complications
1242  *  caused by hardware scrolling. Top level functions:
1243  *
1244  *      fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins()
1245  *
1246  *  handles y values in range [0, scr_height-1] that correspond to real
1247  *  screen positions. y_wrap shift means that first line of bitmap may be
1248  *  anywhere on this display. These functions convert lineoffsets to
1249  *  bitmap offsets and deal with the wrap-around case by splitting blits.
1250  *
1251  *      fbcon_bmove_physical_8()    -- These functions fast implementations
1252  *      fbcon_clear_physical_8()    -- of original fbcon_XXX fns.
1253  *      fbcon_putc_physical_8()     -- (font width != 8) may be added later
1254  *
1255  *  WARNING:
1256  *
1257  *  At the moment fbcon_putc() cannot blit across vertical wrap boundary
1258  *  Implies should only really hardware scroll in rows. Only reason for
1259  *  restriction is simplicity & efficiency at the moment.
1260  */
1261
1262 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
1263                         int width)
1264 {
1265         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1266         struct fbcon_ops *ops = info->fbcon_par;
1267
1268         struct fbcon_display *p = &fb_display[vc->vc_num];
1269         u_int y_break;
1270
1271         if (fbcon_is_inactive(vc, info))
1272                 return;
1273
1274         if (!height || !width)
1275                 return;
1276
1277         if (sy < vc->vc_top && vc->vc_top == logo_lines) {
1278                 vc->vc_top = 0;
1279                 /*
1280                  * If the font dimensions are not an integral of the display
1281                  * dimensions then the ops->clear below won't end up clearing
1282                  * the margins.  Call clear_margins here in case the logo
1283                  * bitmap stretched into the margin area.
1284                  */
1285                 fbcon_clear_margins(vc, 0);
1286         }
1287
1288         /* Split blits that cross physical y_wrap boundary */
1289
1290         y_break = p->vrows - p->yscroll;
1291         if (sy < y_break && sy + height - 1 >= y_break) {
1292                 u_int b = y_break - sy;
1293                 ops->clear(vc, info, real_y(p, sy), sx, b, width);
1294                 ops->clear(vc, info, real_y(p, sy + b), sx, height - b,
1295                                  width);
1296         } else
1297                 ops->clear(vc, info, real_y(p, sy), sx, height, width);
1298 }
1299
1300 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
1301                         int count, int ypos, int xpos)
1302 {
1303         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1304         struct fbcon_display *p = &fb_display[vc->vc_num];
1305         struct fbcon_ops *ops = info->fbcon_par;
1306
1307         if (!fbcon_is_inactive(vc, info))
1308                 ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
1309                            get_color(vc, info, scr_readw(s), 1),
1310                            get_color(vc, info, scr_readw(s), 0));
1311 }
1312
1313 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
1314 {
1315         unsigned short chr;
1316
1317         scr_writew(c, &chr);
1318         fbcon_putcs(vc, &chr, 1, ypos, xpos);
1319 }
1320
1321 static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)
1322 {
1323         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1324         struct fbcon_ops *ops = info->fbcon_par;
1325
1326         if (!fbcon_is_inactive(vc, info))
1327                 ops->clear_margins(vc, info, margin_color, bottom_only);
1328 }
1329
1330 static void fbcon_cursor(struct vc_data *vc, int mode)
1331 {
1332         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1333         struct fbcon_ops *ops = info->fbcon_par;
1334         int c = scr_readw((u16 *) vc->vc_pos);
1335
1336         ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
1337
1338         if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1)
1339                 return;
1340
1341         if (vc->vc_cursor_type & CUR_SW)
1342                 fbcon_del_cursor_timer(info);
1343         else
1344                 fbcon_add_cursor_timer(info);
1345
1346         ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1;
1347
1348         ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
1349                     get_color(vc, info, c, 0));
1350 }
1351
1352 static int scrollback_phys_max = 0;
1353 static int scrollback_max = 0;
1354 static int scrollback_current = 0;
1355
1356 static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
1357                            int unit)
1358 {
1359         struct fbcon_display *p, *t;
1360         struct vc_data **default_mode, *vc;
1361         struct vc_data *svc;
1362         struct fbcon_ops *ops = info->fbcon_par;
1363         int rows, cols, charcnt = 256;
1364
1365         p = &fb_display[unit];
1366
1367         if (var_to_display(p, var, info))
1368                 return;
1369
1370         vc = vc_cons[unit].d;
1371
1372         if (!vc)
1373                 return;
1374
1375         default_mode = vc->vc_display_fg;
1376         svc = *default_mode;
1377         t = &fb_display[svc->vc_num];
1378
1379         if (!vc->vc_font.data) {
1380                 vc->vc_font.data = (void *)(p->fontdata = t->fontdata);
1381                 vc->vc_font.width = (*default_mode)->vc_font.width;
1382                 vc->vc_font.height = (*default_mode)->vc_font.height;
1383                 p->userfont = t->userfont;
1384                 if (p->userfont)
1385                         REFCOUNT(p->fontdata)++;
1386         }
1387         if (p->userfont)
1388                 charcnt = FNTCHARCNT(p->fontdata);
1389
1390         var->activate = FB_ACTIVATE_NOW;
1391         info->var.activate = var->activate;
1392         var->yoffset = info->var.yoffset;
1393         var->xoffset = info->var.xoffset;
1394         fb_set_var(info, var);
1395         ops->var = info->var;
1396         vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1397         vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1398         if (charcnt == 256) {
1399                 vc->vc_hi_font_mask = 0;
1400         } else {
1401                 vc->vc_hi_font_mask = 0x100;
1402                 if (vc->vc_can_do_color)
1403                         vc->vc_complement_mask <<= 1;
1404         }
1405
1406         if (!*svc->vc_uni_pagedir_loc)
1407                 con_set_default_unimap(svc);
1408         if (!*vc->vc_uni_pagedir_loc)
1409                 con_copy_unimap(vc, svc);
1410
1411         cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1412         rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1413         cols /= vc->vc_font.width;
1414         rows /= vc->vc_font.height;
1415         vc_resize(vc, cols, rows);
1416
1417         if (con_is_visible(vc)) {
1418                 update_screen(vc);
1419         }
1420 }
1421
1422 static __inline__ void ywrap_up(struct vc_data *vc, int count)
1423 {
1424         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1425         struct fbcon_ops *ops = info->fbcon_par;
1426         struct fbcon_display *p = &fb_display[vc->vc_num];
1427         
1428         p->yscroll += count;
1429         if (p->yscroll >= p->vrows)     /* Deal with wrap */
1430                 p->yscroll -= p->vrows;
1431         ops->var.xoffset = 0;
1432         ops->var.yoffset = p->yscroll * vc->vc_font.height;
1433         ops->var.vmode |= FB_VMODE_YWRAP;
1434         ops->update_start(info);
1435         scrollback_max += count;
1436         if (scrollback_max > scrollback_phys_max)
1437                 scrollback_max = scrollback_phys_max;
1438         scrollback_current = 0;
1439 }
1440
1441 static __inline__ void ywrap_down(struct vc_data *vc, int count)
1442 {
1443         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1444         struct fbcon_ops *ops = info->fbcon_par;
1445         struct fbcon_display *p = &fb_display[vc->vc_num];
1446         
1447         p->yscroll -= count;
1448         if (p->yscroll < 0)     /* Deal with wrap */
1449                 p->yscroll += p->vrows;
1450         ops->var.xoffset = 0;
1451         ops->var.yoffset = p->yscroll * vc->vc_font.height;
1452         ops->var.vmode |= FB_VMODE_YWRAP;
1453         ops->update_start(info);
1454         scrollback_max -= count;
1455         if (scrollback_max < 0)
1456                 scrollback_max = 0;
1457         scrollback_current = 0;
1458 }
1459
1460 static __inline__ void ypan_up(struct vc_data *vc, int count)
1461 {
1462         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1463         struct fbcon_display *p = &fb_display[vc->vc_num];
1464         struct fbcon_ops *ops = info->fbcon_par;
1465
1466         p->yscroll += count;
1467         if (p->yscroll > p->vrows - vc->vc_rows) {
1468                 ops->bmove(vc, info, p->vrows - vc->vc_rows,
1469                             0, 0, 0, vc->vc_rows, vc->vc_cols);
1470                 p->yscroll -= p->vrows - vc->vc_rows;
1471         }
1472
1473         ops->var.xoffset = 0;
1474         ops->var.yoffset = p->yscroll * vc->vc_font.height;
1475         ops->var.vmode &= ~FB_VMODE_YWRAP;
1476         ops->update_start(info);
1477         fbcon_clear_margins(vc, 1);
1478         scrollback_max += count;
1479         if (scrollback_max > scrollback_phys_max)
1480                 scrollback_max = scrollback_phys_max;
1481         scrollback_current = 0;
1482 }
1483
1484 static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
1485 {
1486         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1487         struct fbcon_ops *ops = info->fbcon_par;
1488         struct fbcon_display *p = &fb_display[vc->vc_num];
1489
1490         p->yscroll += count;
1491
1492         if (p->yscroll > p->vrows - vc->vc_rows) {
1493                 p->yscroll -= p->vrows - vc->vc_rows;
1494                 fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
1495         }
1496
1497         ops->var.xoffset = 0;
1498         ops->var.yoffset = p->yscroll * vc->vc_font.height;
1499         ops->var.vmode &= ~FB_VMODE_YWRAP;
1500         ops->update_start(info);
1501         fbcon_clear_margins(vc, 1);
1502         scrollback_max += count;
1503         if (scrollback_max > scrollback_phys_max)
1504                 scrollback_max = scrollback_phys_max;
1505         scrollback_current = 0;
1506 }
1507
1508 static __inline__ void ypan_down(struct vc_data *vc, int count)
1509 {
1510         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1511         struct fbcon_display *p = &fb_display[vc->vc_num];
1512         struct fbcon_ops *ops = info->fbcon_par;
1513         
1514         p->yscroll -= count;
1515         if (p->yscroll < 0) {
1516                 ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
1517                             0, vc->vc_rows, vc->vc_cols);
1518                 p->yscroll += p->vrows - vc->vc_rows;
1519         }
1520
1521         ops->var.xoffset = 0;
1522         ops->var.yoffset = p->yscroll * vc->vc_font.height;
1523         ops->var.vmode &= ~FB_VMODE_YWRAP;
1524         ops->update_start(info);
1525         fbcon_clear_margins(vc, 1);
1526         scrollback_max -= count;
1527         if (scrollback_max < 0)
1528                 scrollback_max = 0;
1529         scrollback_current = 0;
1530 }
1531
1532 static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
1533 {
1534         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1535         struct fbcon_ops *ops = info->fbcon_par;
1536         struct fbcon_display *p = &fb_display[vc->vc_num];
1537
1538         p->yscroll -= count;
1539
1540         if (p->yscroll < 0) {
1541                 p->yscroll += p->vrows - vc->vc_rows;
1542                 fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
1543         }
1544
1545         ops->var.xoffset = 0;
1546         ops->var.yoffset = p->yscroll * vc->vc_font.height;
1547         ops->var.vmode &= ~FB_VMODE_YWRAP;
1548         ops->update_start(info);
1549         fbcon_clear_margins(vc, 1);
1550         scrollback_max -= count;
1551         if (scrollback_max < 0)
1552                 scrollback_max = 0;
1553         scrollback_current = 0;
1554 }
1555
1556 static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
1557                               int line, int count, int dy)
1558 {
1559         unsigned short *s = (unsigned short *)
1560                 (vc->vc_origin + vc->vc_size_row * line);
1561
1562         while (count--) {
1563                 unsigned short *start = s;
1564                 unsigned short *le = advance_row(s, 1);
1565                 unsigned short c;
1566                 int x = 0;
1567                 unsigned short attr = 1;
1568
1569                 do {
1570                         c = scr_readw(s);
1571                         if (attr != (c & 0xff00)) {
1572                                 attr = c & 0xff00;
1573                                 if (s > start) {
1574                                         fbcon_putcs(vc, start, s - start,
1575                                                     dy, x);
1576                                         x += s - start;
1577                                         start = s;
1578                                 }
1579                         }
1580                         console_conditional_schedule();
1581                         s++;
1582                 } while (s < le);
1583                 if (s > start)
1584                         fbcon_putcs(vc, start, s - start, dy, x);
1585                 console_conditional_schedule();
1586                 dy++;
1587         }
1588 }
1589
1590 static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info,
1591                         struct fbcon_display *p, int line, int count, int ycount)
1592 {
1593         int offset = ycount * vc->vc_cols;
1594         unsigned short *d = (unsigned short *)
1595             (vc->vc_origin + vc->vc_size_row * line);
1596         unsigned short *s = d + offset;
1597         struct fbcon_ops *ops = info->fbcon_par;
1598
1599         while (count--) {
1600                 unsigned short *start = s;
1601                 unsigned short *le = advance_row(s, 1);
1602                 unsigned short c;
1603                 int x = 0;
1604
1605                 do {
1606                         c = scr_readw(s);
1607
1608                         if (c == scr_readw(d)) {
1609                                 if (s > start) {
1610                                         ops->bmove(vc, info, line + ycount, x,
1611                                                    line, x, 1, s-start);
1612                                         x += s - start + 1;
1613                                         start = s + 1;
1614                                 } else {
1615                                         x++;
1616                                         start++;
1617                                 }
1618                         }
1619
1620                         scr_writew(c, d);
1621                         console_conditional_schedule();
1622                         s++;
1623                         d++;
1624                 } while (s < le);
1625                 if (s > start)
1626                         ops->bmove(vc, info, line + ycount, x, line, x, 1,
1627                                    s-start);
1628                 console_conditional_schedule();
1629                 if (ycount > 0)
1630                         line++;
1631                 else {
1632                         line--;
1633                         /* NOTE: We subtract two lines from these pointers */
1634                         s -= vc->vc_size_row;
1635                         d -= vc->vc_size_row;
1636                 }
1637         }
1638 }
1639
1640 static void fbcon_redraw(struct vc_data *vc, struct fbcon_display *p,
1641                          int line, int count, int offset)
1642 {
1643         unsigned short *d = (unsigned short *)
1644             (vc->vc_origin + vc->vc_size_row * line);
1645         unsigned short *s = d + offset;
1646
1647         while (count--) {
1648                 unsigned short *start = s;
1649                 unsigned short *le = advance_row(s, 1);
1650                 unsigned short c;
1651                 int x = 0;
1652                 unsigned short attr = 1;
1653
1654                 do {
1655                         c = scr_readw(s);
1656                         if (attr != (c & 0xff00)) {
1657                                 attr = c & 0xff00;
1658                                 if (s > start) {
1659                                         fbcon_putcs(vc, start, s - start,
1660                                                     line, x);
1661                                         x += s - start;
1662                                         start = s;
1663                                 }
1664                         }
1665                         if (c == scr_readw(d)) {
1666                                 if (s > start) {
1667                                         fbcon_putcs(vc, start, s - start,
1668                                                      line, x);
1669                                         x += s - start + 1;
1670                                         start = s + 1;
1671                                 } else {
1672                                         x++;
1673                                         start++;
1674                                 }
1675                         }
1676                         scr_writew(c, d);
1677                         console_conditional_schedule();
1678                         s++;
1679                         d++;
1680                 } while (s < le);
1681                 if (s > start)
1682                         fbcon_putcs(vc, start, s - start, line, x);
1683                 console_conditional_schedule();
1684                 if (offset > 0)
1685                         line++;
1686                 else {
1687                         line--;
1688                         /* NOTE: We subtract two lines from these pointers */
1689                         s -= vc->vc_size_row;
1690                         d -= vc->vc_size_row;
1691                 }
1692         }
1693 }
1694
1695 static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
1696                 enum con_scroll dir, unsigned int count)
1697 {
1698         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1699         struct fbcon_display *p = &fb_display[vc->vc_num];
1700         int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
1701
1702         if (fbcon_is_inactive(vc, info))
1703                 return true;
1704
1705         fbcon_cursor(vc, CM_ERASE);
1706
1707         /*
1708          * ++Geert: Only use ywrap/ypan if the console is in text mode
1709          * ++Andrew: Only use ypan on hardware text mode when scrolling the
1710          *           whole screen (prevents flicker).
1711          */
1712
1713         switch (dir) {
1714         case SM_UP:
1715                 if (count > vc->vc_rows)        /* Maximum realistic size */
1716                         count = vc->vc_rows;
1717                 if (logo_shown >= 0)
1718                         goto redraw_up;
1719                 switch (p->scrollmode) {
1720                 case SCROLL_MOVE:
1721                         fbcon_redraw_blit(vc, info, p, t, b - t - count,
1722                                      count);
1723                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1724                         scr_memsetw((unsigned short *) (vc->vc_origin +
1725                                                         vc->vc_size_row *
1726                                                         (b - count)),
1727                                     vc->vc_video_erase_char,
1728                                     vc->vc_size_row * count);
1729                         return true;
1730                         break;
1731
1732                 case SCROLL_WRAP_MOVE:
1733                         if (b - t - count > 3 * vc->vc_rows >> 2) {
1734                                 if (t > 0)
1735                                         fbcon_bmove(vc, 0, 0, count, 0, t,
1736                                                     vc->vc_cols);
1737                                 ywrap_up(vc, count);
1738                                 if (vc->vc_rows - b > 0)
1739                                         fbcon_bmove(vc, b - count, 0, b, 0,
1740                                                     vc->vc_rows - b,
1741                                                     vc->vc_cols);
1742                         } else if (info->flags & FBINFO_READS_FAST)
1743                                 fbcon_bmove(vc, t + count, 0, t, 0,
1744                                             b - t - count, vc->vc_cols);
1745                         else
1746                                 goto redraw_up;
1747                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1748                         break;
1749
1750                 case SCROLL_PAN_REDRAW:
1751                         if ((p->yscroll + count <=
1752                              2 * (p->vrows - vc->vc_rows))
1753                             && ((!scroll_partial && (b - t == vc->vc_rows))
1754                                 || (scroll_partial
1755                                     && (b - t - count >
1756                                         3 * vc->vc_rows >> 2)))) {
1757                                 if (t > 0)
1758                                         fbcon_redraw_move(vc, p, 0, t, count);
1759                                 ypan_up_redraw(vc, t, count);
1760                                 if (vc->vc_rows - b > 0)
1761                                         fbcon_redraw_move(vc, p, b,
1762                                                           vc->vc_rows - b, b);
1763                         } else
1764                                 fbcon_redraw_move(vc, p, t + count, b - t - count, t);
1765                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1766                         break;
1767
1768                 case SCROLL_PAN_MOVE:
1769                         if ((p->yscroll + count <=
1770                              2 * (p->vrows - vc->vc_rows))
1771                             && ((!scroll_partial && (b - t == vc->vc_rows))
1772                                 || (scroll_partial
1773                                     && (b - t - count >
1774                                         3 * vc->vc_rows >> 2)))) {
1775                                 if (t > 0)
1776                                         fbcon_bmove(vc, 0, 0, count, 0, t,
1777                                                     vc->vc_cols);
1778                                 ypan_up(vc, count);
1779                                 if (vc->vc_rows - b > 0)
1780                                         fbcon_bmove(vc, b - count, 0, b, 0,
1781                                                     vc->vc_rows - b,
1782                                                     vc->vc_cols);
1783                         } else if (info->flags & FBINFO_READS_FAST)
1784                                 fbcon_bmove(vc, t + count, 0, t, 0,
1785                                             b - t - count, vc->vc_cols);
1786                         else
1787                                 goto redraw_up;
1788                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1789                         break;
1790
1791                 case SCROLL_REDRAW:
1792                       redraw_up:
1793                         fbcon_redraw(vc, p, t, b - t - count,
1794                                      count * vc->vc_cols);
1795                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1796                         scr_memsetw((unsigned short *) (vc->vc_origin +
1797                                                         vc->vc_size_row *
1798                                                         (b - count)),
1799                                     vc->vc_video_erase_char,
1800                                     vc->vc_size_row * count);
1801                         return true;
1802                 }
1803                 break;
1804
1805         case SM_DOWN:
1806                 if (count > vc->vc_rows)        /* Maximum realistic size */
1807                         count = vc->vc_rows;
1808                 if (logo_shown >= 0)
1809                         goto redraw_down;
1810                 switch (p->scrollmode) {
1811                 case SCROLL_MOVE:
1812                         fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
1813                                      -count);
1814                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
1815                         scr_memsetw((unsigned short *) (vc->vc_origin +
1816                                                         vc->vc_size_row *
1817                                                         t),
1818                                     vc->vc_video_erase_char,
1819                                     vc->vc_size_row * count);
1820                         return true;
1821                         break;
1822
1823                 case SCROLL_WRAP_MOVE:
1824                         if (b - t - count > 3 * vc->vc_rows >> 2) {
1825                                 if (vc->vc_rows - b > 0)
1826                                         fbcon_bmove(vc, b, 0, b - count, 0,
1827                                                     vc->vc_rows - b,
1828                                                     vc->vc_cols);
1829                                 ywrap_down(vc, count);
1830                                 if (t > 0)
1831                                         fbcon_bmove(vc, count, 0, 0, 0, t,
1832                                                     vc->vc_cols);
1833                         } else if (info->flags & FBINFO_READS_FAST)
1834                                 fbcon_bmove(vc, t, 0, t + count, 0,
1835                                             b - t - count, vc->vc_cols);
1836                         else
1837                                 goto redraw_down;
1838                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
1839                         break;
1840
1841                 case SCROLL_PAN_MOVE:
1842                         if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1843                             && ((!scroll_partial && (b - t == vc->vc_rows))
1844                                 || (scroll_partial
1845                                     && (b - t - count >
1846                                         3 * vc->vc_rows >> 2)))) {
1847                                 if (vc->vc_rows - b > 0)
1848                                         fbcon_bmove(vc, b, 0, b - count, 0,
1849                                                     vc->vc_rows - b,
1850                                                     vc->vc_cols);
1851                                 ypan_down(vc, count);
1852                                 if (t > 0)
1853                                         fbcon_bmove(vc, count, 0, 0, 0, t,
1854                                                     vc->vc_cols);
1855                         } else if (info->flags & FBINFO_READS_FAST)
1856                                 fbcon_bmove(vc, t, 0, t + count, 0,
1857                                             b - t - count, vc->vc_cols);
1858                         else
1859                                 goto redraw_down;
1860                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
1861                         break;
1862
1863                 case SCROLL_PAN_REDRAW:
1864                         if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1865                             && ((!scroll_partial && (b - t == vc->vc_rows))
1866                                 || (scroll_partial
1867                                     && (b - t - count >
1868                                         3 * vc->vc_rows >> 2)))) {
1869                                 if (vc->vc_rows - b > 0)
1870                                         fbcon_redraw_move(vc, p, b, vc->vc_rows - b,
1871                                                           b - count);
1872                                 ypan_down_redraw(vc, t, count);
1873                                 if (t > 0)
1874                                         fbcon_redraw_move(vc, p, count, t, 0);
1875                         } else
1876                                 fbcon_redraw_move(vc, p, t, b - t - count, t + count);
1877                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
1878                         break;
1879
1880                 case SCROLL_REDRAW:
1881                       redraw_down:
1882                         fbcon_redraw(vc, p, b - 1, b - t - count,
1883                                      -count * vc->vc_cols);
1884                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
1885                         scr_memsetw((unsigned short *) (vc->vc_origin +
1886                                                         vc->vc_size_row *
1887                                                         t),
1888                                     vc->vc_video_erase_char,
1889                                     vc->vc_size_row * count);
1890                         return true;
1891                 }
1892         }
1893         return false;
1894 }
1895
1896
1897 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
1898                         int height, int width)
1899 {
1900         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1901         struct fbcon_display *p = &fb_display[vc->vc_num];
1902         
1903         if (fbcon_is_inactive(vc, info))
1904                 return;
1905
1906         if (!width || !height)
1907                 return;
1908
1909         /*  Split blits that cross physical y_wrap case.
1910          *  Pathological case involves 4 blits, better to use recursive
1911          *  code rather than unrolled case
1912          *
1913          *  Recursive invocations don't need to erase the cursor over and
1914          *  over again, so we use fbcon_bmove_rec()
1915          */
1916         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
1917                         p->vrows - p->yscroll);
1918 }
1919
1920 static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx,
1921                             int dy, int dx, int height, int width, u_int y_break)
1922 {
1923         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1924         struct fbcon_ops *ops = info->fbcon_par;
1925         u_int b;
1926
1927         if (sy < y_break && sy + height > y_break) {
1928                 b = y_break - sy;
1929                 if (dy < sy) {  /* Avoid trashing self */
1930                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1931                                         y_break);
1932                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1933                                         height - b, width, y_break);
1934                 } else {
1935                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1936                                         height - b, width, y_break);
1937                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1938                                         y_break);
1939                 }
1940                 return;
1941         }
1942
1943         if (dy < y_break && dy + height > y_break) {
1944                 b = y_break - dy;
1945                 if (dy < sy) {  /* Avoid trashing self */
1946                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1947                                         y_break);
1948                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1949                                         height - b, width, y_break);
1950                 } else {
1951                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1952                                         height - b, width, y_break);
1953                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1954                                         y_break);
1955                 }
1956                 return;
1957         }
1958         ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
1959                    height, width);
1960 }
1961
1962 static void updatescrollmode(struct fbcon_display *p,
1963                                         struct fb_info *info,
1964                                         struct vc_data *vc)
1965 {
1966         struct fbcon_ops *ops = info->fbcon_par;
1967         int fh = vc->vc_font.height;
1968         int cap = info->flags;
1969         u16 t = 0;
1970         int ypan = FBCON_SWAP(ops->rotate, info->fix.ypanstep,
1971                                   info->fix.xpanstep);
1972         int ywrap = FBCON_SWAP(ops->rotate, info->fix.ywrapstep, t);
1973         int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1974         int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
1975                                    info->var.xres_virtual);
1976         int good_pan = (cap & FBINFO_HWACCEL_YPAN) &&
1977                 divides(ypan, vc->vc_font.height) && vyres > yres;
1978         int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) &&
1979                 divides(ywrap, vc->vc_font.height) &&
1980                 divides(vc->vc_font.height, vyres) &&
1981                 divides(vc->vc_font.height, yres);
1982         int reading_fast = cap & FBINFO_READS_FAST;
1983         int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) &&
1984                 !(cap & FBINFO_HWACCEL_DISABLED);
1985         int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) &&
1986                 !(cap & FBINFO_HWACCEL_DISABLED);
1987
1988         p->vrows = vyres/fh;
1989         if (yres > (fh * (vc->vc_rows + 1)))
1990                 p->vrows -= (yres - (fh * vc->vc_rows)) / fh;
1991         if ((yres % fh) && (vyres % fh < yres % fh))
1992                 p->vrows--;
1993
1994         if (good_wrap || good_pan) {
1995                 if (reading_fast || fast_copyarea)
1996                         p->scrollmode = good_wrap ?
1997                                 SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
1998                 else
1999                         p->scrollmode = good_wrap ? SCROLL_REDRAW :
2000                                 SCROLL_PAN_REDRAW;
2001         } else {
2002                 if (reading_fast || (fast_copyarea && !fast_imageblit))
2003                         p->scrollmode = SCROLL_MOVE;
2004                 else
2005                         p->scrollmode = SCROLL_REDRAW;
2006         }
2007 }
2008
2009 #define PITCH(w) (((w) + 7) >> 3)
2010 #define CALC_FONTSZ(h, p, c) ((h) * (p) * (c)) /* size = height * pitch * charcount */
2011
2012 static int fbcon_resize(struct vc_data *vc, unsigned int width, 
2013                         unsigned int height, unsigned int user)
2014 {
2015         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2016         struct fbcon_ops *ops = info->fbcon_par;
2017         struct fbcon_display *p = &fb_display[vc->vc_num];
2018         struct fb_var_screeninfo var = info->var;
2019         int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
2020
2021         if (p->userfont && FNTSIZE(vc->vc_font.data)) {
2022                 int size;
2023                 int pitch = PITCH(vc->vc_font.width);
2024
2025                 /*
2026                  * If user font, ensure that a possible change to user font
2027                  * height or width will not allow a font data out-of-bounds access.
2028                  * NOTE: must use original charcount in calculation as font
2029                  * charcount can change and cannot be used to determine the
2030                  * font data allocated size.
2031                  */
2032                 if (pitch <= 0)
2033                         return -EINVAL;
2034                 size = CALC_FONTSZ(vc->vc_font.height, pitch, FNTCHARCNT(vc->vc_font.data));
2035                 if (size > FNTSIZE(vc->vc_font.data))
2036                         return -EINVAL;
2037         }
2038
2039         virt_w = FBCON_SWAP(ops->rotate, width, height);
2040         virt_h = FBCON_SWAP(ops->rotate, height, width);
2041         virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width,
2042                                  vc->vc_font.height);
2043         virt_fh = FBCON_SWAP(ops->rotate, vc->vc_font.height,
2044                                  vc->vc_font.width);
2045         var.xres = virt_w * virt_fw;
2046         var.yres = virt_h * virt_fh;
2047         x_diff = info->var.xres - var.xres;
2048         y_diff = info->var.yres - var.yres;
2049         if (x_diff < 0 || x_diff > virt_fw ||
2050             y_diff < 0 || y_diff > virt_fh) {
2051                 const struct fb_videomode *mode;
2052
2053                 DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
2054                 mode = fb_find_best_mode(&var, &info->modelist);
2055                 if (mode == NULL)
2056                         return -EINVAL;
2057                 display_to_var(&var, p);
2058                 fb_videomode_to_var(&var, mode);
2059
2060                 if (virt_w > var.xres/virt_fw || virt_h > var.yres/virt_fh)
2061                         return -EINVAL;
2062
2063                 DPRINTK("resize now %ix%i\n", var.xres, var.yres);
2064                 if (con_is_visible(vc)) {
2065                         var.activate = FB_ACTIVATE_NOW |
2066                                 FB_ACTIVATE_FORCE;
2067                         fb_set_var(info, &var);
2068                 }
2069                 var_to_display(p, &info->var, info);
2070                 ops->var = info->var;
2071         }
2072         updatescrollmode(p, info, vc);
2073         return 0;
2074 }
2075
2076 static int fbcon_switch(struct vc_data *vc)
2077 {
2078         struct fb_info *info, *old_info = NULL;
2079         struct fbcon_ops *ops;
2080         struct fbcon_display *p = &fb_display[vc->vc_num];
2081         struct fb_var_screeninfo var;
2082         int i, ret, prev_console, charcnt = 256;
2083
2084         info = registered_fb[con2fb_map[vc->vc_num]];
2085         ops = info->fbcon_par;
2086
2087         if (logo_shown >= 0) {
2088                 struct vc_data *conp2 = vc_cons[logo_shown].d;
2089
2090                 if (conp2->vc_top == logo_lines
2091                     && conp2->vc_bottom == conp2->vc_rows)
2092                         conp2->vc_top = 0;
2093                 logo_shown = FBCON_LOGO_CANSHOW;
2094         }
2095
2096         prev_console = ops->currcon;
2097         if (prev_console != -1)
2098                 old_info = registered_fb[con2fb_map[prev_console]];
2099         /*
2100          * FIXME: If we have multiple fbdev's loaded, we need to
2101          * update all info->currcon.  Perhaps, we can place this
2102          * in a centralized structure, but this might break some
2103          * drivers.
2104          *
2105          * info->currcon = vc->vc_num;
2106          */
2107         for_each_registered_fb(i) {
2108                 if (registered_fb[i]->fbcon_par) {
2109                         struct fbcon_ops *o = registered_fb[i]->fbcon_par;
2110
2111                         o->currcon = vc->vc_num;
2112                 }
2113         }
2114         memset(&var, 0, sizeof(struct fb_var_screeninfo));
2115         display_to_var(&var, p);
2116         var.activate = FB_ACTIVATE_NOW;
2117
2118         /*
2119          * make sure we don't unnecessarily trip the memcmp()
2120          * in fb_set_var()
2121          */
2122         info->var.activate = var.activate;
2123         var.vmode |= info->var.vmode & ~FB_VMODE_MASK;
2124         fb_set_var(info, &var);
2125         ops->var = info->var;
2126
2127         if (old_info != NULL && (old_info != info ||
2128                                  info->flags & FBINFO_MISC_ALWAYS_SETPAR)) {
2129                 if (info->fbops->fb_set_par) {
2130                         ret = info->fbops->fb_set_par(info);
2131
2132                         if (ret)
2133                                 printk(KERN_ERR "fbcon_switch: detected "
2134                                         "unhandled fb_set_par error, "
2135                                         "error code %d\n", ret);
2136                 }
2137
2138                 if (old_info != info)
2139                         fbcon_del_cursor_timer(old_info);
2140         }
2141
2142         if (fbcon_is_inactive(vc, info) ||
2143             ops->blank_state != FB_BLANK_UNBLANK)
2144                 fbcon_del_cursor_timer(info);
2145         else
2146                 fbcon_add_cursor_timer(info);
2147
2148         set_blitting_type(vc, info);
2149         ops->cursor_reset = 1;
2150
2151         if (ops->rotate_font && ops->rotate_font(info, vc)) {
2152                 ops->rotate = FB_ROTATE_UR;
2153                 set_blitting_type(vc, info);
2154         }
2155
2156         vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
2157         vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
2158
2159         if (p->userfont)
2160                 charcnt = FNTCHARCNT(vc->vc_font.data);
2161
2162         if (charcnt > 256)
2163                 vc->vc_complement_mask <<= 1;
2164
2165         updatescrollmode(p, info, vc);
2166
2167         switch (p->scrollmode) {
2168         case SCROLL_WRAP_MOVE:
2169                 scrollback_phys_max = p->vrows - vc->vc_rows;
2170                 break;
2171         case SCROLL_PAN_MOVE:
2172         case SCROLL_PAN_REDRAW:
2173                 scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
2174                 if (scrollback_phys_max < 0)
2175                         scrollback_phys_max = 0;
2176                 break;
2177         default:
2178                 scrollback_phys_max = 0;
2179                 break;
2180         }
2181
2182         scrollback_max = 0;
2183         scrollback_current = 0;
2184
2185         if (!fbcon_is_inactive(vc, info)) {
2186             ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
2187             ops->update_start(info);
2188         }
2189
2190         fbcon_set_palette(vc, color_table);     
2191         fbcon_clear_margins(vc, 0);
2192
2193         if (logo_shown == FBCON_LOGO_DRAW) {
2194
2195                 logo_shown = fg_console;
2196                 /* This is protected above by initmem_freed */
2197                 fb_show_logo(info, ops->rotate);
2198                 update_region(vc,
2199                               vc->vc_origin + vc->vc_size_row * vc->vc_top,
2200                               vc->vc_size_row * (vc->vc_bottom -
2201                                                  vc->vc_top) / 2);
2202                 return 0;
2203         }
2204         return 1;
2205 }
2206
2207 static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
2208                                 int blank)
2209 {
2210         if (blank) {
2211                 unsigned short charmask = vc->vc_hi_font_mask ?
2212                         0x1ff : 0xff;
2213                 unsigned short oldc;
2214
2215                 oldc = vc->vc_video_erase_char;
2216                 vc->vc_video_erase_char &= charmask;
2217                 fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols);
2218                 vc->vc_video_erase_char = oldc;
2219         }
2220 }
2221
2222 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
2223 {
2224         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2225         struct fbcon_ops *ops = info->fbcon_par;
2226
2227         if (mode_switch) {
2228                 struct fb_var_screeninfo var = info->var;
2229
2230                 ops->graphics = 1;
2231
2232                 if (!blank) {
2233                         var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE |
2234                                 FB_ACTIVATE_KD_TEXT;
2235                         fb_set_var(info, &var);
2236                         ops->graphics = 0;
2237                         ops->var = info->var;
2238                 }
2239         }
2240
2241         if (!fbcon_is_inactive(vc, info)) {
2242                 if (ops->blank_state != blank) {
2243                         ops->blank_state = blank;
2244                         fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
2245                         ops->cursor_flash = (!blank);
2246
2247                         if (fb_blank(info, blank))
2248                                 fbcon_generic_blank(vc, info, blank);
2249                 }
2250
2251                 if (!blank)
2252                         update_screen(vc);
2253         }
2254
2255         if (mode_switch || fbcon_is_inactive(vc, info) ||
2256             ops->blank_state != FB_BLANK_UNBLANK)
2257                 fbcon_del_cursor_timer(info);
2258         else
2259                 fbcon_add_cursor_timer(info);
2260
2261         return 0;
2262 }
2263
2264 static int fbcon_debug_enter(struct vc_data *vc)
2265 {
2266         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2267         struct fbcon_ops *ops = info->fbcon_par;
2268
2269         ops->save_graphics = ops->graphics;
2270         ops->graphics = 0;
2271         if (info->fbops->fb_debug_enter)
2272                 info->fbops->fb_debug_enter(info);
2273         fbcon_set_palette(vc, color_table);
2274         return 0;
2275 }
2276
2277 static int fbcon_debug_leave(struct vc_data *vc)
2278 {
2279         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2280         struct fbcon_ops *ops = info->fbcon_par;
2281
2282         ops->graphics = ops->save_graphics;
2283         if (info->fbops->fb_debug_leave)
2284                 info->fbops->fb_debug_leave(info);
2285         return 0;
2286 }
2287
2288 static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
2289 {
2290         u8 *fontdata = vc->vc_font.data;
2291         u8 *data = font->data;
2292         int i, j;
2293
2294         font->width = vc->vc_font.width;
2295         font->height = vc->vc_font.height;
2296         font->charcount = vc->vc_hi_font_mask ? 512 : 256;
2297         if (!font->data)
2298                 return 0;
2299
2300         if (font->width <= 8) {
2301                 j = vc->vc_font.height;
2302                 if (font->charcount * j > FNTSIZE(fontdata))
2303                         return -EINVAL;
2304
2305                 for (i = 0; i < font->charcount; i++) {
2306                         memcpy(data, fontdata, j);
2307                         memset(data + j, 0, 32 - j);
2308                         data += 32;
2309                         fontdata += j;
2310                 }
2311         } else if (font->width <= 16) {
2312                 j = vc->vc_font.height * 2;
2313                 if (font->charcount * j > FNTSIZE(fontdata))
2314                         return -EINVAL;
2315
2316                 for (i = 0; i < font->charcount; i++) {
2317                         memcpy(data, fontdata, j);
2318                         memset(data + j, 0, 64 - j);
2319                         data += 64;
2320                         fontdata += j;
2321                 }
2322         } else if (font->width <= 24) {
2323                 if (font->charcount * (vc->vc_font.height * sizeof(u32)) > FNTSIZE(fontdata))
2324                         return -EINVAL;
2325
2326                 for (i = 0; i < font->charcount; i++) {
2327                         for (j = 0; j < vc->vc_font.height; j++) {
2328                                 *data++ = fontdata[0];
2329                                 *data++ = fontdata[1];
2330                                 *data++ = fontdata[2];
2331                                 fontdata += sizeof(u32);
2332                         }
2333                         memset(data, 0, 3 * (32 - j));
2334                         data += 3 * (32 - j);
2335                 }
2336         } else {
2337                 j = vc->vc_font.height * 4;
2338                 if (font->charcount * j > FNTSIZE(fontdata))
2339                         return -EINVAL;
2340
2341                 for (i = 0; i < font->charcount; i++) {
2342                         memcpy(data, fontdata, j);
2343                         memset(data + j, 0, 128 - j);
2344                         data += 128;
2345                         fontdata += j;
2346                 }
2347         }
2348         return 0;
2349 }
2350
2351 /* set/clear vc_hi_font_mask and update vc attrs accordingly */
2352 static void set_vc_hi_font(struct vc_data *vc, bool set)
2353 {
2354         if (!set) {
2355                 vc->vc_hi_font_mask = 0;
2356                 if (vc->vc_can_do_color) {
2357                         vc->vc_complement_mask >>= 1;
2358                         vc->vc_s_complement_mask >>= 1;
2359                 }
2360                         
2361                 /* ++Edmund: reorder the attribute bits */
2362                 if (vc->vc_can_do_color) {
2363                         unsigned short *cp =
2364                             (unsigned short *) vc->vc_origin;
2365                         int count = vc->vc_screenbuf_size / 2;
2366                         unsigned short c;
2367                         for (; count > 0; count--, cp++) {
2368                                 c = scr_readw(cp);
2369                                 scr_writew(((c & 0xfe00) >> 1) |
2370                                            (c & 0xff), cp);
2371                         }
2372                         c = vc->vc_video_erase_char;
2373                         vc->vc_video_erase_char =
2374                             ((c & 0xfe00) >> 1) | (c & 0xff);
2375                         vc->vc_attr >>= 1;
2376                 }
2377         } else {
2378                 vc->vc_hi_font_mask = 0x100;
2379                 if (vc->vc_can_do_color) {
2380                         vc->vc_complement_mask <<= 1;
2381                         vc->vc_s_complement_mask <<= 1;
2382                 }
2383                         
2384                 /* ++Edmund: reorder the attribute bits */
2385                 {
2386                         unsigned short *cp =
2387                             (unsigned short *) vc->vc_origin;
2388                         int count = vc->vc_screenbuf_size / 2;
2389                         unsigned short c;
2390                         for (; count > 0; count--, cp++) {
2391                                 unsigned short newc;
2392                                 c = scr_readw(cp);
2393                                 if (vc->vc_can_do_color)
2394                                         newc =
2395                                             ((c & 0xff00) << 1) | (c &
2396                                                                    0xff);
2397                                 else
2398                                         newc = c & ~0x100;
2399                                 scr_writew(newc, cp);
2400                         }
2401                         c = vc->vc_video_erase_char;
2402                         if (vc->vc_can_do_color) {
2403                                 vc->vc_video_erase_char =
2404                                     ((c & 0xff00) << 1) | (c & 0xff);
2405                                 vc->vc_attr <<= 1;
2406                         } else
2407                                 vc->vc_video_erase_char = c & ~0x100;
2408                 }
2409         }
2410 }
2411
2412 static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
2413                              const u8 * data, int userfont)
2414 {
2415         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2416         struct fbcon_ops *ops = info->fbcon_par;
2417         struct fbcon_display *p = &fb_display[vc->vc_num];
2418         int resize;
2419         int cnt;
2420         char *old_data = NULL;
2421
2422         resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
2423         if (p->userfont)
2424                 old_data = vc->vc_font.data;
2425         if (userfont)
2426                 cnt = FNTCHARCNT(data);
2427         else
2428                 cnt = 256;
2429         vc->vc_font.data = (void *)(p->fontdata = data);
2430         if ((p->userfont = userfont))
2431                 REFCOUNT(data)++;
2432         vc->vc_font.width = w;
2433         vc->vc_font.height = h;
2434         if (vc->vc_hi_font_mask && cnt == 256)
2435                 set_vc_hi_font(vc, false);
2436         else if (!vc->vc_hi_font_mask && cnt == 512)
2437                 set_vc_hi_font(vc, true);
2438
2439         if (resize) {
2440                 int cols, rows;
2441
2442                 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2443                 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2444                 cols /= w;
2445                 rows /= h;
2446                 vc_resize(vc, cols, rows);
2447         } else if (con_is_visible(vc)
2448                    && vc->vc_mode == KD_TEXT) {
2449                 fbcon_clear_margins(vc, 0);
2450                 update_screen(vc);
2451         }
2452
2453         if (old_data && (--REFCOUNT(old_data) == 0))
2454                 kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
2455         return 0;
2456 }
2457
2458 static int fbcon_copy_font(struct vc_data *vc, int con)
2459 {
2460         struct fbcon_display *od = &fb_display[con];
2461         struct console_font *f = &vc->vc_font;
2462
2463         if (od->fontdata == f->data)
2464                 return 0;       /* already the same font... */
2465         return fbcon_do_set_font(vc, f->width, f->height, od->fontdata, od->userfont);
2466 }
2467
2468 /*
2469  *  User asked to set font; we are guaranteed that
2470  *      a) width and height are in range 1..32
2471  *      b) charcount does not exceed 512
2472  *  but lets not assume that, since someone might someday want to use larger
2473  *  fonts. And charcount of 512 is small for unicode support.
2474  *
2475  *  However, user space gives the font in 32 rows , regardless of
2476  *  actual font height. So a new API is needed if support for larger fonts
2477  *  is ever implemented.
2478  */
2479
2480 static int fbcon_set_font(struct vc_data *vc, struct console_font *font,
2481                           unsigned int flags)
2482 {
2483         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2484         unsigned charcount = font->charcount;
2485         int w = font->width;
2486         int h = font->height;
2487         int size;
2488         int i, csum;
2489         u8 *new_data, *data = font->data;
2490         int pitch = PITCH(font->width);
2491
2492         /* Is there a reason why fbconsole couldn't handle any charcount >256?
2493          * If not this check should be changed to charcount < 256 */
2494         if (charcount != 256 && charcount != 512)
2495                 return -EINVAL;
2496
2497         /* Make sure drawing engine can handle the font */
2498         if (!(info->pixmap.blit_x & (1 << (font->width - 1))) ||
2499             !(info->pixmap.blit_y & (1 << (font->height - 1))))
2500                 return -EINVAL;
2501
2502         /* Make sure driver can handle the font length */
2503         if (fbcon_invalid_charcount(info, charcount))
2504                 return -EINVAL;
2505
2506         size = CALC_FONTSZ(h, pitch, charcount);
2507
2508         new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER);
2509
2510         if (!new_data)
2511                 return -ENOMEM;
2512
2513         new_data += FONT_EXTRA_WORDS * sizeof(int);
2514         FNTSIZE(new_data) = size;
2515         FNTCHARCNT(new_data) = charcount;
2516         REFCOUNT(new_data) = 0; /* usage counter */
2517         for (i=0; i< charcount; i++) {
2518                 memcpy(new_data + i*h*pitch, data +  i*32*pitch, h*pitch);
2519         }
2520
2521         /* Since linux has a nice crc32 function use it for counting font
2522          * checksums. */
2523         csum = crc32(0, new_data, size);
2524
2525         FNTSUM(new_data) = csum;
2526         /* Check if the same font is on some other console already */
2527         for (i = first_fb_vc; i <= last_fb_vc; i++) {
2528                 struct vc_data *tmp = vc_cons[i].d;
2529                 
2530                 if (fb_display[i].userfont &&
2531                     fb_display[i].fontdata &&
2532                     FNTSUM(fb_display[i].fontdata) == csum &&
2533                     FNTSIZE(fb_display[i].fontdata) == size &&
2534                     tmp->vc_font.width == w &&
2535                     !memcmp(fb_display[i].fontdata, new_data, size)) {
2536                         kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
2537                         new_data = (u8 *)fb_display[i].fontdata;
2538                         break;
2539                 }
2540         }
2541         return fbcon_do_set_font(vc, font->width, font->height, new_data, 1);
2542 }
2543
2544 static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name)
2545 {
2546         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2547         const struct font_desc *f;
2548
2549         if (!name)
2550                 f = get_default_font(info->var.xres, info->var.yres,
2551                                      info->pixmap.blit_x, info->pixmap.blit_y);
2552         else if (!(f = find_font(name)))
2553                 return -ENOENT;
2554
2555         font->width = f->width;
2556         font->height = f->height;
2557         return fbcon_do_set_font(vc, f->width, f->height, f->data, 0);
2558 }
2559
2560 static u16 palette_red[16];
2561 static u16 palette_green[16];
2562 static u16 palette_blue[16];
2563
2564 static struct fb_cmap palette_cmap = {
2565         0, 16, palette_red, palette_green, palette_blue, NULL
2566 };
2567
2568 static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table)
2569 {
2570         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2571         int i, j, k, depth;
2572         u8 val;
2573
2574         if (fbcon_is_inactive(vc, info))
2575                 return;
2576
2577         if (!con_is_visible(vc))
2578                 return;
2579
2580         depth = fb_get_color_depth(&info->var, &info->fix);
2581         if (depth > 3) {
2582                 for (i = j = 0; i < 16; i++) {
2583                         k = table[i];
2584                         val = vc->vc_palette[j++];
2585                         palette_red[k] = (val << 8) | val;
2586                         val = vc->vc_palette[j++];
2587                         palette_green[k] = (val << 8) | val;
2588                         val = vc->vc_palette[j++];
2589                         palette_blue[k] = (val << 8) | val;
2590                 }
2591                 palette_cmap.len = 16;
2592                 palette_cmap.start = 0;
2593         /*
2594          * If framebuffer is capable of less than 16 colors,
2595          * use default palette of fbcon.
2596          */
2597         } else
2598                 fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap);
2599
2600         fb_set_cmap(&palette_cmap, info);
2601 }
2602
2603 static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
2604 {
2605         return (u16 *) (vc->vc_origin + offset);
2606 }
2607
2608 static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
2609                                  int *px, int *py)
2610 {
2611         unsigned long ret;
2612         int x, y;
2613
2614         if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
2615                 unsigned long offset = (pos - vc->vc_origin) / 2;
2616
2617                 x = offset % vc->vc_cols;
2618                 y = offset / vc->vc_cols;
2619                 ret = pos + (vc->vc_cols - x) * 2;
2620         } else {
2621                 /* Should not happen */
2622                 x = y = 0;
2623                 ret = vc->vc_origin;
2624         }
2625         if (px)
2626                 *px = x;
2627         if (py)
2628                 *py = y;
2629         return ret;
2630 }
2631
2632 /* As we might be inside of softback, we may work with non-contiguous buffer,
2633    that's why we have to use a separate routine. */
2634 static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
2635 {
2636         while (cnt--) {
2637                 u16 a = scr_readw(p);
2638                 if (!vc->vc_can_do_color)
2639                         a ^= 0x0800;
2640                 else if (vc->vc_hi_font_mask == 0x100)
2641                         a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
2642                             (((a) & 0x0e00) << 4);
2643                 else
2644                         a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
2645                             (((a) & 0x0700) << 4);
2646                 scr_writew(a, p++);
2647         }
2648 }
2649
2650 static int fbcon_set_origin(struct vc_data *vc)
2651 {
2652         return 0;
2653 }
2654
2655 void fbcon_suspended(struct fb_info *info)
2656 {
2657         struct vc_data *vc = NULL;
2658         struct fbcon_ops *ops = info->fbcon_par;
2659
2660         if (!ops || ops->currcon < 0)
2661                 return;
2662         vc = vc_cons[ops->currcon].d;
2663
2664         /* Clear cursor, restore saved data */
2665         fbcon_cursor(vc, CM_ERASE);
2666 }
2667
2668 void fbcon_resumed(struct fb_info *info)
2669 {
2670         struct vc_data *vc;
2671         struct fbcon_ops *ops = info->fbcon_par;
2672
2673         if (!ops || ops->currcon < 0)
2674                 return;
2675         vc = vc_cons[ops->currcon].d;
2676
2677         update_screen(vc);
2678 }
2679
2680 static void fbcon_modechanged(struct fb_info *info)
2681 {
2682         struct fbcon_ops *ops = info->fbcon_par;
2683         struct vc_data *vc;
2684         struct fbcon_display *p;
2685         int rows, cols;
2686
2687         if (!ops || ops->currcon < 0)
2688                 return;
2689         vc = vc_cons[ops->currcon].d;
2690         if (vc->vc_mode != KD_TEXT ||
2691             registered_fb[con2fb_map[ops->currcon]] != info)
2692                 return;
2693
2694         p = &fb_display[vc->vc_num];
2695         set_blitting_type(vc, info);
2696
2697         if (con_is_visible(vc)) {
2698                 var_to_display(p, &info->var, info);
2699                 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2700                 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2701                 cols /= vc->vc_font.width;
2702                 rows /= vc->vc_font.height;
2703                 vc_resize(vc, cols, rows);
2704                 updatescrollmode(p, info, vc);
2705                 scrollback_max = 0;
2706                 scrollback_current = 0;
2707
2708                 if (!fbcon_is_inactive(vc, info)) {
2709                     ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
2710                     ops->update_start(info);
2711                 }
2712
2713                 fbcon_set_palette(vc, color_table);
2714                 update_screen(vc);
2715         }
2716 }
2717
2718 static void fbcon_set_all_vcs(struct fb_info *info)
2719 {
2720         struct fbcon_ops *ops = info->fbcon_par;
2721         struct vc_data *vc;
2722         struct fbcon_display *p;
2723         int i, rows, cols, fg = -1;
2724
2725         if (!ops || ops->currcon < 0)
2726                 return;
2727
2728         for (i = first_fb_vc; i <= last_fb_vc; i++) {
2729                 vc = vc_cons[i].d;
2730                 if (!vc || vc->vc_mode != KD_TEXT ||
2731                     registered_fb[con2fb_map[i]] != info)
2732                         continue;
2733
2734                 if (con_is_visible(vc)) {
2735                         fg = i;
2736                         continue;
2737                 }
2738
2739                 p = &fb_display[vc->vc_num];
2740                 set_blitting_type(vc, info);
2741                 var_to_display(p, &info->var, info);
2742                 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2743                 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2744                 cols /= vc->vc_font.width;
2745                 rows /= vc->vc_font.height;
2746                 vc_resize(vc, cols, rows);
2747         }
2748
2749         if (fg != -1)
2750                 fbcon_modechanged(info);
2751 }
2752
2753
2754 void fbcon_update_vcs(struct fb_info *info, bool all)
2755 {
2756         if (all)
2757                 fbcon_set_all_vcs(info);
2758         else
2759                 fbcon_modechanged(info);
2760 }
2761 EXPORT_SYMBOL(fbcon_update_vcs);
2762
2763 int fbcon_mode_deleted(struct fb_info *info,
2764                        struct fb_videomode *mode)
2765 {
2766         struct fb_info *fb_info;
2767         struct fbcon_display *p;
2768         int i, j, found = 0;
2769
2770         /* before deletion, ensure that mode is not in use */
2771         for (i = first_fb_vc; i <= last_fb_vc; i++) {
2772                 j = con2fb_map[i];
2773                 if (j == -1)
2774                         continue;
2775                 fb_info = registered_fb[j];
2776                 if (fb_info != info)
2777                         continue;
2778                 p = &fb_display[i];
2779                 if (!p || !p->mode)
2780                         continue;
2781                 if (fb_mode_is_equal(p->mode, mode)) {
2782                         found = 1;
2783                         break;
2784                 }
2785         }
2786         return found;
2787 }
2788
2789 #ifdef CONFIG_VT_HW_CONSOLE_BINDING
2790 static void fbcon_unbind(void)
2791 {
2792         int ret;
2793
2794         ret = do_unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc,
2795                                 fbcon_is_default);
2796
2797         if (!ret)
2798                 fbcon_has_console_bind = 0;
2799 }
2800 #else
2801 static inline void fbcon_unbind(void) {}
2802 #endif /* CONFIG_VT_HW_CONSOLE_BINDING */
2803
2804 /* called with console_lock held */
2805 void fbcon_fb_unbind(struct fb_info *info)
2806 {
2807         int i, new_idx = -1, ret = 0;
2808         int idx = info->node;
2809
2810         WARN_CONSOLE_UNLOCKED();
2811
2812         if (!fbcon_has_console_bind)
2813                 return;
2814
2815         for (i = first_fb_vc; i <= last_fb_vc; i++) {
2816                 if (con2fb_map[i] != idx &&
2817                     con2fb_map[i] != -1) {
2818                         new_idx = con2fb_map[i];
2819                         break;
2820                 }
2821         }
2822
2823         if (new_idx != -1) {
2824                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2825                         if (con2fb_map[i] == idx)
2826                                 set_con2fb_map(i, new_idx, 0);
2827                 }
2828         } else {
2829                 struct fb_info *info = registered_fb[idx];
2830
2831                 /* This is sort of like set_con2fb_map, except it maps
2832                  * the consoles to no device and then releases the
2833                  * oldinfo to free memory and cancel the cursor blink
2834                  * timer. I can imagine this just becoming part of
2835                  * set_con2fb_map where new_idx is -1
2836                  */
2837                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2838                         if (con2fb_map[i] == idx) {
2839                                 con2fb_map[i] = -1;
2840                                 if (!search_fb_in_map(idx)) {
2841                                         ret = con2fb_release_oldinfo(vc_cons[i].d,
2842                                                                      info, NULL, i,
2843                                                                      idx, 0);
2844                                         if (ret) {
2845                                                 con2fb_map[i] = idx;
2846                                                 return;
2847                                         }
2848                                 }
2849                         }
2850                 }
2851                 fbcon_unbind();
2852         }
2853 }
2854
2855 /* called with console_lock held */
2856 void fbcon_fb_unregistered(struct fb_info *info)
2857 {
2858         int i, idx;
2859
2860         WARN_CONSOLE_UNLOCKED();
2861
2862         if (deferred_takeover)
2863                 return;
2864
2865         idx = info->node;
2866         for (i = first_fb_vc; i <= last_fb_vc; i++) {
2867                 if (con2fb_map[i] == idx)
2868                         con2fb_map[i] = -1;
2869         }
2870
2871         if (idx == info_idx) {
2872                 info_idx = -1;
2873
2874                 for_each_registered_fb(i) {
2875                         info_idx = i;
2876                         break;
2877                 }
2878         }
2879
2880         if (info_idx != -1) {
2881                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2882                         if (con2fb_map[i] == -1)
2883                                 con2fb_map[i] = info_idx;
2884                 }
2885         }
2886
2887         if (primary_device == idx)
2888                 primary_device = -1;
2889
2890         if (!num_registered_fb)
2891                 do_unregister_con_driver(&fb_con);
2892 }
2893
2894 void fbcon_remap_all(struct fb_info *info)
2895 {
2896         int i, idx = info->node;
2897
2898         console_lock();
2899         if (deferred_takeover) {
2900                 for (i = first_fb_vc; i <= last_fb_vc; i++)
2901                         con2fb_map_boot[i] = idx;
2902                 fbcon_map_override();
2903                 console_unlock();
2904                 return;
2905         }
2906
2907         for (i = first_fb_vc; i <= last_fb_vc; i++)
2908                 set_con2fb_map(i, idx, 0);
2909
2910         if (con_is_bound(&fb_con)) {
2911                 printk(KERN_INFO "fbcon: Remapping primary device, "
2912                        "fb%i, to tty %i-%i\n", idx,
2913                        first_fb_vc + 1, last_fb_vc + 1);
2914                 info_idx = idx;
2915         }
2916         console_unlock();
2917 }
2918
2919 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
2920 static void fbcon_select_primary(struct fb_info *info)
2921 {
2922         if (!map_override && primary_device == -1 &&
2923             fb_is_primary_device(info)) {
2924                 int i;
2925
2926                 printk(KERN_INFO "fbcon: %s (fb%i) is primary device\n",
2927                        info->fix.id, info->node);
2928                 primary_device = info->node;
2929
2930                 for (i = first_fb_vc; i <= last_fb_vc; i++)
2931                         con2fb_map_boot[i] = primary_device;
2932
2933                 if (con_is_bound(&fb_con)) {
2934                         printk(KERN_INFO "fbcon: Remapping primary device, "
2935                                "fb%i, to tty %i-%i\n", info->node,
2936                                first_fb_vc + 1, last_fb_vc + 1);
2937                         info_idx = primary_device;
2938                 }
2939         }
2940
2941 }
2942 #else
2943 static inline void fbcon_select_primary(struct fb_info *info)
2944 {
2945         return;
2946 }
2947 #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */
2948
2949 /* called with console_lock held */
2950 int fbcon_fb_registered(struct fb_info *info)
2951 {
2952         int ret = 0, i, idx;
2953
2954         WARN_CONSOLE_UNLOCKED();
2955
2956         idx = info->node;
2957         fbcon_select_primary(info);
2958
2959         if (deferred_takeover) {
2960                 pr_info("fbcon: Deferring console take-over\n");
2961                 return 0;
2962         }
2963
2964         if (info_idx == -1) {
2965                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2966                         if (con2fb_map_boot[i] == idx) {
2967                                 info_idx = idx;
2968                                 break;
2969                         }
2970                 }
2971
2972                 if (info_idx != -1)
2973                         ret = do_fbcon_takeover(1);
2974         } else {
2975                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2976                         if (con2fb_map_boot[i] == idx)
2977                                 set_con2fb_map(i, idx, 0);
2978                 }
2979         }
2980
2981         return ret;
2982 }
2983
2984 void fbcon_fb_blanked(struct fb_info *info, int blank)
2985 {
2986         struct fbcon_ops *ops = info->fbcon_par;
2987         struct vc_data *vc;
2988
2989         if (!ops || ops->currcon < 0)
2990                 return;
2991
2992         vc = vc_cons[ops->currcon].d;
2993         if (vc->vc_mode != KD_TEXT ||
2994                         registered_fb[con2fb_map[ops->currcon]] != info)
2995                 return;
2996
2997         if (con_is_visible(vc)) {
2998                 if (blank)
2999                         do_blank_screen(0);
3000                 else
3001                         do_unblank_screen(0);
3002         }
3003         ops->blank_state = blank;
3004 }
3005
3006 void fbcon_new_modelist(struct fb_info *info)
3007 {
3008         int i;
3009         struct vc_data *vc;
3010         struct fb_var_screeninfo var;
3011         const struct fb_videomode *mode;
3012
3013         for (i = first_fb_vc; i <= last_fb_vc; i++) {
3014                 if (registered_fb[con2fb_map[i]] != info)
3015                         continue;
3016                 if (!fb_display[i].mode)
3017                         continue;
3018                 vc = vc_cons[i].d;
3019                 display_to_var(&var, &fb_display[i]);
3020                 mode = fb_find_nearest_mode(fb_display[i].mode,
3021                                             &info->modelist);
3022                 fb_videomode_to_var(&var, mode);
3023                 fbcon_set_disp(info, &var, vc->vc_num);
3024         }
3025 }
3026
3027 void fbcon_get_requirement(struct fb_info *info,
3028                            struct fb_blit_caps *caps)
3029 {
3030         struct vc_data *vc;
3031         struct fbcon_display *p;
3032
3033         if (caps->flags) {
3034                 int i, charcnt;
3035
3036                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
3037                         vc = vc_cons[i].d;
3038                         if (vc && vc->vc_mode == KD_TEXT &&
3039                             info->node == con2fb_map[i]) {
3040                                 p = &fb_display[i];
3041                                 caps->x |= 1 << (vc->vc_font.width - 1);
3042                                 caps->y |= 1 << (vc->vc_font.height - 1);
3043                                 charcnt = (p->userfont) ?
3044                                         FNTCHARCNT(p->fontdata) : 256;
3045                                 if (caps->len < charcnt)
3046                                         caps->len = charcnt;
3047                         }
3048                 }
3049         } else {
3050                 vc = vc_cons[fg_console].d;
3051
3052                 if (vc && vc->vc_mode == KD_TEXT &&
3053                     info->node == con2fb_map[fg_console]) {
3054                         p = &fb_display[fg_console];
3055                         caps->x = 1 << (vc->vc_font.width - 1);
3056                         caps->y = 1 << (vc->vc_font.height - 1);
3057                         caps->len = (p->userfont) ?
3058                                 FNTCHARCNT(p->fontdata) : 256;
3059                 }
3060         }
3061 }
3062
3063 int fbcon_set_con2fb_map_ioctl(void __user *argp)
3064 {
3065         struct fb_con2fbmap con2fb;
3066         int ret;
3067
3068         if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3069                 return -EFAULT;
3070         if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3071                 return -EINVAL;
3072         if (con2fb.framebuffer >= FB_MAX)
3073                 return -EINVAL;
3074         if (!registered_fb[con2fb.framebuffer])
3075                 request_module("fb%d", con2fb.framebuffer);
3076         if (!registered_fb[con2fb.framebuffer]) {
3077                 return -EINVAL;
3078         }
3079
3080         console_lock();
3081         ret = set_con2fb_map(con2fb.console - 1,
3082                              con2fb.framebuffer, 1);
3083         console_unlock();
3084
3085         return ret;
3086 }
3087
3088 int fbcon_get_con2fb_map_ioctl(void __user *argp)
3089 {
3090         struct fb_con2fbmap con2fb;
3091
3092         if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3093                 return -EFAULT;
3094         if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3095                 return -EINVAL;
3096
3097         console_lock();
3098         con2fb.framebuffer = con2fb_map[con2fb.console - 1];
3099         console_unlock();
3100
3101         return copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
3102 }
3103
3104 /*
3105  *  The console `switch' structure for the frame buffer based console
3106  */
3107
3108 static const struct consw fb_con = {
3109         .owner                  = THIS_MODULE,
3110         .con_startup            = fbcon_startup,
3111         .con_init               = fbcon_init,
3112         .con_deinit             = fbcon_deinit,
3113         .con_clear              = fbcon_clear,
3114         .con_putc               = fbcon_putc,
3115         .con_putcs              = fbcon_putcs,
3116         .con_cursor             = fbcon_cursor,
3117         .con_scroll             = fbcon_scroll,
3118         .con_switch             = fbcon_switch,
3119         .con_blank              = fbcon_blank,
3120         .con_font_set           = fbcon_set_font,
3121         .con_font_get           = fbcon_get_font,
3122         .con_font_default       = fbcon_set_def_font,
3123         .con_font_copy          = fbcon_copy_font,
3124         .con_set_palette        = fbcon_set_palette,
3125         .con_set_origin         = fbcon_set_origin,
3126         .con_invert_region      = fbcon_invert_region,
3127         .con_screen_pos         = fbcon_screen_pos,
3128         .con_getxy              = fbcon_getxy,
3129         .con_resize             = fbcon_resize,
3130         .con_debug_enter        = fbcon_debug_enter,
3131         .con_debug_leave        = fbcon_debug_leave,
3132 };
3133
3134 static ssize_t store_rotate(struct device *device,
3135                             struct device_attribute *attr, const char *buf,
3136                             size_t count)
3137 {
3138         struct fb_info *info;
3139         int rotate, idx;
3140         char **last = NULL;
3141
3142         console_lock();
3143         idx = con2fb_map[fg_console];
3144
3145         if (idx == -1 || registered_fb[idx] == NULL)
3146                 goto err;
3147
3148         info = registered_fb[idx];
3149         rotate = simple_strtoul(buf, last, 0);
3150         fbcon_rotate(info, rotate);
3151 err:
3152         console_unlock();
3153         return count;
3154 }
3155
3156 static ssize_t store_rotate_all(struct device *device,
3157                                 struct device_attribute *attr,const char *buf,
3158                                 size_t count)
3159 {
3160         struct fb_info *info;
3161         int rotate, idx;
3162         char **last = NULL;
3163
3164         console_lock();
3165         idx = con2fb_map[fg_console];
3166
3167         if (idx == -1 || registered_fb[idx] == NULL)
3168                 goto err;
3169
3170         info = registered_fb[idx];
3171         rotate = simple_strtoul(buf, last, 0);
3172         fbcon_rotate_all(info, rotate);
3173 err:
3174         console_unlock();
3175         return count;
3176 }
3177
3178 static ssize_t show_rotate(struct device *device,
3179                            struct device_attribute *attr,char *buf)
3180 {
3181         struct fb_info *info;
3182         int rotate = 0, idx;
3183
3184         console_lock();
3185         idx = con2fb_map[fg_console];
3186
3187         if (idx == -1 || registered_fb[idx] == NULL)
3188                 goto err;
3189
3190         info = registered_fb[idx];
3191         rotate = fbcon_get_rotate(info);
3192 err:
3193         console_unlock();
3194         return snprintf(buf, PAGE_SIZE, "%d\n", rotate);
3195 }
3196
3197 static ssize_t show_cursor_blink(struct device *device,
3198                                  struct device_attribute *attr, char *buf)
3199 {
3200         struct fb_info *info;
3201         struct fbcon_ops *ops;
3202         int idx, blink = -1;
3203
3204         console_lock();
3205         idx = con2fb_map[fg_console];
3206
3207         if (idx == -1 || registered_fb[idx] == NULL)
3208                 goto err;
3209
3210         info = registered_fb[idx];
3211         ops = info->fbcon_par;
3212
3213         if (!ops)
3214                 goto err;
3215
3216         blink = (ops->flags & FBCON_FLAGS_CURSOR_TIMER) ? 1 : 0;
3217 err:
3218         console_unlock();
3219         return snprintf(buf, PAGE_SIZE, "%d\n", blink);
3220 }
3221
3222 static ssize_t store_cursor_blink(struct device *device,
3223                                   struct device_attribute *attr,
3224                                   const char *buf, size_t count)
3225 {
3226         struct fb_info *info;
3227         int blink, idx;
3228         char **last = NULL;
3229
3230         console_lock();
3231         idx = con2fb_map[fg_console];
3232
3233         if (idx == -1 || registered_fb[idx] == NULL)
3234                 goto err;
3235
3236         info = registered_fb[idx];
3237
3238         if (!info->fbcon_par)
3239                 goto err;
3240
3241         blink = simple_strtoul(buf, last, 0);
3242
3243         if (blink) {
3244                 fbcon_cursor_noblink = 0;
3245                 fbcon_add_cursor_timer(info);
3246         } else {
3247                 fbcon_cursor_noblink = 1;
3248                 fbcon_del_cursor_timer(info);
3249         }
3250
3251 err:
3252         console_unlock();
3253         return count;
3254 }
3255
3256 static struct device_attribute device_attrs[] = {
3257         __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
3258         __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all),
3259         __ATTR(cursor_blink, S_IRUGO|S_IWUSR, show_cursor_blink,
3260                store_cursor_blink),
3261 };
3262
3263 static int fbcon_init_device(void)
3264 {
3265         int i, error = 0;
3266
3267         fbcon_has_sysfs = 1;
3268
3269         for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
3270                 error = device_create_file(fbcon_device, &device_attrs[i]);
3271
3272                 if (error)
3273                         break;
3274         }
3275
3276         if (error) {
3277                 while (--i >= 0)
3278                         device_remove_file(fbcon_device, &device_attrs[i]);
3279
3280                 fbcon_has_sysfs = 0;
3281         }
3282
3283         return 0;
3284 }
3285
3286 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3287 static void fbcon_register_existing_fbs(struct work_struct *work)
3288 {
3289         int i;
3290
3291         console_lock();
3292
3293         for_each_registered_fb(i)
3294                 fbcon_fb_registered(registered_fb[i]);
3295
3296         console_unlock();
3297 }
3298
3299 static struct notifier_block fbcon_output_nb;
3300 static DECLARE_WORK(fbcon_deferred_takeover_work, fbcon_register_existing_fbs);
3301
3302 static int fbcon_output_notifier(struct notifier_block *nb,
3303                                  unsigned long action, void *data)
3304 {
3305         WARN_CONSOLE_UNLOCKED();
3306
3307         pr_info("fbcon: Taking over console\n");
3308
3309         dummycon_unregister_output_notifier(&fbcon_output_nb);
3310         deferred_takeover = false;
3311         logo_shown = FBCON_LOGO_DONTSHOW;
3312
3313         /* We may get called in atomic context */
3314         schedule_work(&fbcon_deferred_takeover_work);
3315
3316         return NOTIFY_OK;
3317 }
3318 #endif
3319
3320 static void fbcon_start(void)
3321 {
3322         WARN_CONSOLE_UNLOCKED();
3323
3324 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3325         if (conswitchp != &dummy_con)
3326                 deferred_takeover = false;
3327
3328         if (deferred_takeover) {
3329                 fbcon_output_nb.notifier_call = fbcon_output_notifier;
3330                 dummycon_register_output_notifier(&fbcon_output_nb);
3331                 return;
3332         }
3333 #endif
3334
3335         if (num_registered_fb) {
3336                 int i;
3337
3338                 for_each_registered_fb(i) {
3339                         info_idx = i;
3340                         break;
3341                 }
3342
3343                 do_fbcon_takeover(0);
3344         }
3345 }
3346
3347 static void fbcon_exit(void)
3348 {
3349         struct fb_info *info;
3350         int i, j, mapped;
3351
3352 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3353         if (deferred_takeover) {
3354                 dummycon_unregister_output_notifier(&fbcon_output_nb);
3355                 deferred_takeover = false;
3356         }
3357 #endif
3358
3359         for_each_registered_fb(i) {
3360                 int pending = 0;
3361
3362                 mapped = 0;
3363                 info = registered_fb[i];
3364
3365                 if (info->queue.func)
3366                         pending = cancel_work_sync(&info->queue);
3367                 DPRINTK("fbcon: %s pending work\n", (pending ? "canceled" :
3368                         "no"));
3369
3370                 for (j = first_fb_vc; j <= last_fb_vc; j++) {
3371                         if (con2fb_map[j] == i) {
3372                                 mapped = 1;
3373                                 con2fb_map[j] = -1;
3374                         }
3375                 }
3376
3377                 if (mapped) {
3378                         if (info->fbops->fb_release)
3379                                 info->fbops->fb_release(info, 0);
3380                         module_put(info->fbops->owner);
3381
3382                         if (info->fbcon_par) {
3383                                 struct fbcon_ops *ops = info->fbcon_par;
3384
3385                                 fbcon_del_cursor_timer(info);
3386                                 kfree(ops->cursor_src);
3387                                 kfree(ops->cursor_state.mask);
3388                                 kfree(info->fbcon_par);
3389                                 info->fbcon_par = NULL;
3390                         }
3391
3392                         if (info->queue.func == fb_flashcursor)
3393                                 info->queue.func = NULL;
3394                 }
3395         }
3396 }
3397
3398 void __init fb_console_init(void)
3399 {
3400         int i;
3401
3402         console_lock();
3403         fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
3404                                      "fbcon");
3405
3406         if (IS_ERR(fbcon_device)) {
3407                 printk(KERN_WARNING "Unable to create device "
3408                        "for fbcon; errno = %ld\n",
3409                        PTR_ERR(fbcon_device));
3410                 fbcon_device = NULL;
3411         } else
3412                 fbcon_init_device();
3413
3414         for (i = 0; i < MAX_NR_CONSOLES; i++)
3415                 con2fb_map[i] = -1;
3416
3417         fbcon_start();
3418         console_unlock();
3419 }
3420
3421 #ifdef MODULE
3422
3423 static void __exit fbcon_deinit_device(void)
3424 {
3425         int i;
3426
3427         if (fbcon_has_sysfs) {
3428                 for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
3429                         device_remove_file(fbcon_device, &device_attrs[i]);
3430
3431                 fbcon_has_sysfs = 0;
3432         }
3433 }
3434
3435 void __exit fb_console_exit(void)
3436 {
3437         console_lock();
3438         fbcon_deinit_device();
3439         device_destroy(fb_class, MKDEV(0, 0));
3440         fbcon_exit();
3441         do_unregister_con_driver(&fb_con);
3442         console_unlock();
3443 }       
3444 #endif