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