cef437817b0dc670e63cf1e8f6ab6512522558b4
[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 cap, 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         cap = info->flags;
1046
1047         if (logo_shown < 0 && console_loglevel <= CONSOLE_LOGLEVEL_QUIET)
1048                 logo_shown = FBCON_LOGO_DONTSHOW;
1049
1050         if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
1051             (info->fix.type == FB_TYPE_TEXT))
1052                 logo = 0;
1053
1054         if (var_to_display(p, &info->var, info))
1055                 return;
1056
1057         if (!info->fbcon_par)
1058                 con2fb_acquire_newinfo(vc, info, vc->vc_num, -1);
1059
1060         /* If we are not the first console on this
1061            fb, copy the font from that console */
1062         t = &fb_display[fg_console];
1063         if (!p->fontdata) {
1064                 if (t->fontdata) {
1065                         struct vc_data *fvc = vc_cons[fg_console].d;
1066
1067                         vc->vc_font.data = (void *)(p->fontdata =
1068                                                     fvc->vc_font.data);
1069                         vc->vc_font.width = fvc->vc_font.width;
1070                         vc->vc_font.height = fvc->vc_font.height;
1071                         p->userfont = t->userfont;
1072
1073                         if (p->userfont)
1074                                 REFCOUNT(p->fontdata)++;
1075                 } else {
1076                         const struct font_desc *font = NULL;
1077
1078                         if (!fontname[0] || !(font = find_font(fontname)))
1079                                 font = get_default_font(info->var.xres,
1080                                                         info->var.yres,
1081                                                         info->pixmap.blit_x,
1082                                                         info->pixmap.blit_y);
1083                         vc->vc_font.width = font->width;
1084                         vc->vc_font.height = font->height;
1085                         vc->vc_font.data = (void *)(p->fontdata = font->data);
1086                         vc->vc_font.charcount = 256; /* FIXME  Need to
1087                                                         support more fonts */
1088                 }
1089         }
1090
1091         if (p->userfont)
1092                 charcnt = FNTCHARCNT(p->fontdata);
1093
1094         vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1095         vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1096         if (charcnt == 256) {
1097                 vc->vc_hi_font_mask = 0;
1098         } else {
1099                 vc->vc_hi_font_mask = 0x100;
1100                 if (vc->vc_can_do_color)
1101                         vc->vc_complement_mask <<= 1;
1102         }
1103
1104         if (!*svc->vc_uni_pagedir_loc)
1105                 con_set_default_unimap(svc);
1106         if (!*vc->vc_uni_pagedir_loc)
1107                 con_copy_unimap(vc, svc);
1108
1109         ops = info->fbcon_par;
1110         ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
1111
1112         p->con_rotate = initial_rotation;
1113         if (p->con_rotate == -1)
1114                 p->con_rotate = info->fbcon_rotate_hint;
1115         if (p->con_rotate == -1)
1116                 p->con_rotate = FB_ROTATE_UR;
1117
1118         set_blitting_type(vc, info);
1119
1120         cols = vc->vc_cols;
1121         rows = vc->vc_rows;
1122         new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1123         new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1124         new_cols /= vc->vc_font.width;
1125         new_rows /= vc->vc_font.height;
1126
1127         /*
1128          * We must always set the mode. The mode of the previous console
1129          * driver could be in the same resolution but we are using different
1130          * hardware so we have to initialize the hardware.
1131          *
1132          * We need to do it in fbcon_init() to prevent screen corruption.
1133          */
1134         if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
1135                 if (info->fbops->fb_set_par &&
1136                     !(ops->flags & FBCON_FLAGS_INIT)) {
1137                         ret = info->fbops->fb_set_par(info);
1138
1139                         if (ret)
1140                                 printk(KERN_ERR "fbcon_init: detected "
1141                                         "unhandled fb_set_par error, "
1142                                         "error code %d\n", ret);
1143                 }
1144
1145                 ops->flags |= FBCON_FLAGS_INIT;
1146         }
1147
1148         ops->graphics = 0;
1149
1150         if ((cap & FBINFO_HWACCEL_COPYAREA) &&
1151             !(cap & FBINFO_HWACCEL_DISABLED))
1152                 p->scrollmode = SCROLL_MOVE;
1153         else /* default to something safe */
1154                 p->scrollmode = SCROLL_REDRAW;
1155
1156         /*
1157          *  ++guenther: console.c:vc_allocate() relies on initializing
1158          *  vc_{cols,rows}, but we must not set those if we are only
1159          *  resizing the console.
1160          */
1161         if (init) {
1162                 vc->vc_cols = new_cols;
1163                 vc->vc_rows = new_rows;
1164         } else
1165                 vc_resize(vc, new_cols, new_rows);
1166
1167         if (logo)
1168                 fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
1169
1170         if (ops->rotate_font && ops->rotate_font(info, vc)) {
1171                 ops->rotate = FB_ROTATE_UR;
1172                 set_blitting_type(vc, info);
1173         }
1174
1175         ops->p = &fb_display[fg_console];
1176 }
1177
1178 static void fbcon_free_font(struct fbcon_display *p, bool freefont)
1179 {
1180         if (freefont && p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
1181                 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
1182         p->fontdata = NULL;
1183         p->userfont = 0;
1184 }
1185
1186 static void set_vc_hi_font(struct vc_data *vc, bool set);
1187
1188 static void fbcon_deinit(struct vc_data *vc)
1189 {
1190         struct fbcon_display *p = &fb_display[vc->vc_num];
1191         struct fb_info *info;
1192         struct fbcon_ops *ops;
1193         int idx;
1194         bool free_font = true;
1195
1196         idx = con2fb_map[vc->vc_num];
1197
1198         if (idx == -1)
1199                 goto finished;
1200
1201         info = registered_fb[idx];
1202
1203         if (!info)
1204                 goto finished;
1205
1206         if (info->flags & FBINFO_MISC_FIRMWARE)
1207                 free_font = false;
1208         ops = info->fbcon_par;
1209
1210         if (!ops)
1211                 goto finished;
1212
1213         if (con_is_visible(vc))
1214                 fbcon_del_cursor_timer(info);
1215
1216         ops->flags &= ~FBCON_FLAGS_INIT;
1217 finished:
1218
1219         fbcon_free_font(p, free_font);
1220         if (free_font)
1221                 vc->vc_font.data = NULL;
1222
1223         if (vc->vc_hi_font_mask && vc->vc_screenbuf)
1224                 set_vc_hi_font(vc, false);
1225
1226         if (!con_is_bound(&fb_con))
1227                 fbcon_exit();
1228
1229         if (vc->vc_num == logo_shown)
1230                 logo_shown = FBCON_LOGO_CANSHOW;
1231
1232         return;
1233 }
1234
1235 /* ====================================================================== */
1236
1237 /*  fbcon_XXX routines - interface used by the world
1238  *
1239  *  This system is now divided into two levels because of complications
1240  *  caused by hardware scrolling. Top level functions:
1241  *
1242  *      fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins()
1243  *
1244  *  handles y values in range [0, scr_height-1] that correspond to real
1245  *  screen positions. y_wrap shift means that first line of bitmap may be
1246  *  anywhere on this display. These functions convert lineoffsets to
1247  *  bitmap offsets and deal with the wrap-around case by splitting blits.
1248  *
1249  *      fbcon_bmove_physical_8()    -- These functions fast implementations
1250  *      fbcon_clear_physical_8()    -- of original fbcon_XXX fns.
1251  *      fbcon_putc_physical_8()     -- (font width != 8) may be added later
1252  *
1253  *  WARNING:
1254  *
1255  *  At the moment fbcon_putc() cannot blit across vertical wrap boundary
1256  *  Implies should only really hardware scroll in rows. Only reason for
1257  *  restriction is simplicity & efficiency at the moment.
1258  */
1259
1260 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
1261                         int width)
1262 {
1263         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1264         struct fbcon_ops *ops = info->fbcon_par;
1265
1266         struct fbcon_display *p = &fb_display[vc->vc_num];
1267         u_int y_break;
1268
1269         if (fbcon_is_inactive(vc, info))
1270                 return;
1271
1272         if (!height || !width)
1273                 return;
1274
1275         if (sy < vc->vc_top && vc->vc_top == logo_lines) {
1276                 vc->vc_top = 0;
1277                 /*
1278                  * If the font dimensions are not an integral of the display
1279                  * dimensions then the ops->clear below won't end up clearing
1280                  * the margins.  Call clear_margins here in case the logo
1281                  * bitmap stretched into the margin area.
1282                  */
1283                 fbcon_clear_margins(vc, 0);
1284         }
1285
1286         /* Split blits that cross physical y_wrap boundary */
1287
1288         y_break = p->vrows - p->yscroll;
1289         if (sy < y_break && sy + height - 1 >= y_break) {
1290                 u_int b = y_break - sy;
1291                 ops->clear(vc, info, real_y(p, sy), sx, b, width);
1292                 ops->clear(vc, info, real_y(p, sy + b), sx, height - b,
1293                                  width);
1294         } else
1295                 ops->clear(vc, info, real_y(p, sy), sx, height, width);
1296 }
1297
1298 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
1299                         int count, int ypos, int xpos)
1300 {
1301         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1302         struct fbcon_display *p = &fb_display[vc->vc_num];
1303         struct fbcon_ops *ops = info->fbcon_par;
1304
1305         if (!fbcon_is_inactive(vc, info))
1306                 ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
1307                            get_color(vc, info, scr_readw(s), 1),
1308                            get_color(vc, info, scr_readw(s), 0));
1309 }
1310
1311 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
1312 {
1313         unsigned short chr;
1314
1315         scr_writew(c, &chr);
1316         fbcon_putcs(vc, &chr, 1, ypos, xpos);
1317 }
1318
1319 static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)
1320 {
1321         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1322         struct fbcon_ops *ops = info->fbcon_par;
1323
1324         if (!fbcon_is_inactive(vc, info))
1325                 ops->clear_margins(vc, info, margin_color, bottom_only);
1326 }
1327
1328 static void fbcon_cursor(struct vc_data *vc, int mode)
1329 {
1330         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1331         struct fbcon_ops *ops = info->fbcon_par;
1332         int c = scr_readw((u16 *) vc->vc_pos);
1333
1334         ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
1335
1336         if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1)
1337                 return;
1338
1339         if (vc->vc_cursor_type & CUR_SW)
1340                 fbcon_del_cursor_timer(info);
1341         else
1342                 fbcon_add_cursor_timer(info);
1343
1344         ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1;
1345
1346         ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
1347                     get_color(vc, info, c, 0));
1348 }
1349
1350 static int scrollback_phys_max = 0;
1351 static int scrollback_max = 0;
1352 static int scrollback_current = 0;
1353
1354 static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
1355                            int unit)
1356 {
1357         struct fbcon_display *p, *t;
1358         struct vc_data **default_mode, *vc;
1359         struct vc_data *svc;
1360         struct fbcon_ops *ops = info->fbcon_par;
1361         int rows, cols, charcnt = 256;
1362
1363         p = &fb_display[unit];
1364
1365         if (var_to_display(p, var, info))
1366                 return;
1367
1368         vc = vc_cons[unit].d;
1369
1370         if (!vc)
1371                 return;
1372
1373         default_mode = vc->vc_display_fg;
1374         svc = *default_mode;
1375         t = &fb_display[svc->vc_num];
1376
1377         if (!vc->vc_font.data) {
1378                 vc->vc_font.data = (void *)(p->fontdata = t->fontdata);
1379                 vc->vc_font.width = (*default_mode)->vc_font.width;
1380                 vc->vc_font.height = (*default_mode)->vc_font.height;
1381                 p->userfont = t->userfont;
1382                 if (p->userfont)
1383                         REFCOUNT(p->fontdata)++;
1384         }
1385         if (p->userfont)
1386                 charcnt = FNTCHARCNT(p->fontdata);
1387
1388         var->activate = FB_ACTIVATE_NOW;
1389         info->var.activate = var->activate;
1390         var->yoffset = info->var.yoffset;
1391         var->xoffset = info->var.xoffset;
1392         fb_set_var(info, var);
1393         ops->var = info->var;
1394         vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1395         vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1396         if (charcnt == 256) {
1397                 vc->vc_hi_font_mask = 0;
1398         } else {
1399                 vc->vc_hi_font_mask = 0x100;
1400                 if (vc->vc_can_do_color)
1401                         vc->vc_complement_mask <<= 1;
1402         }
1403
1404         if (!*svc->vc_uni_pagedir_loc)
1405                 con_set_default_unimap(svc);
1406         if (!*vc->vc_uni_pagedir_loc)
1407                 con_copy_unimap(vc, svc);
1408
1409         cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1410         rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1411         cols /= vc->vc_font.width;
1412         rows /= vc->vc_font.height;
1413         vc_resize(vc, cols, rows);
1414
1415         if (con_is_visible(vc)) {
1416                 update_screen(vc);
1417         }
1418 }
1419
1420 static __inline__ void ywrap_up(struct vc_data *vc, int count)
1421 {
1422         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1423         struct fbcon_ops *ops = info->fbcon_par;
1424         struct fbcon_display *p = &fb_display[vc->vc_num];
1425         
1426         p->yscroll += count;
1427         if (p->yscroll >= p->vrows)     /* Deal with wrap */
1428                 p->yscroll -= p->vrows;
1429         ops->var.xoffset = 0;
1430         ops->var.yoffset = p->yscroll * vc->vc_font.height;
1431         ops->var.vmode |= FB_VMODE_YWRAP;
1432         ops->update_start(info);
1433         scrollback_max += count;
1434         if (scrollback_max > scrollback_phys_max)
1435                 scrollback_max = scrollback_phys_max;
1436         scrollback_current = 0;
1437 }
1438
1439 static __inline__ void ywrap_down(struct vc_data *vc, int count)
1440 {
1441         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1442         struct fbcon_ops *ops = info->fbcon_par;
1443         struct fbcon_display *p = &fb_display[vc->vc_num];
1444         
1445         p->yscroll -= count;
1446         if (p->yscroll < 0)     /* Deal with wrap */
1447                 p->yscroll += p->vrows;
1448         ops->var.xoffset = 0;
1449         ops->var.yoffset = p->yscroll * vc->vc_font.height;
1450         ops->var.vmode |= FB_VMODE_YWRAP;
1451         ops->update_start(info);
1452         scrollback_max -= count;
1453         if (scrollback_max < 0)
1454                 scrollback_max = 0;
1455         scrollback_current = 0;
1456 }
1457
1458 static __inline__ void ypan_up(struct vc_data *vc, int count)
1459 {
1460         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1461         struct fbcon_display *p = &fb_display[vc->vc_num];
1462         struct fbcon_ops *ops = info->fbcon_par;
1463
1464         p->yscroll += count;
1465         if (p->yscroll > p->vrows - vc->vc_rows) {
1466                 ops->bmove(vc, info, p->vrows - vc->vc_rows,
1467                             0, 0, 0, vc->vc_rows, vc->vc_cols);
1468                 p->yscroll -= p->vrows - vc->vc_rows;
1469         }
1470
1471         ops->var.xoffset = 0;
1472         ops->var.yoffset = p->yscroll * vc->vc_font.height;
1473         ops->var.vmode &= ~FB_VMODE_YWRAP;
1474         ops->update_start(info);
1475         fbcon_clear_margins(vc, 1);
1476         scrollback_max += count;
1477         if (scrollback_max > scrollback_phys_max)
1478                 scrollback_max = scrollback_phys_max;
1479         scrollback_current = 0;
1480 }
1481
1482 static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
1483 {
1484         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1485         struct fbcon_ops *ops = info->fbcon_par;
1486         struct fbcon_display *p = &fb_display[vc->vc_num];
1487
1488         p->yscroll += count;
1489
1490         if (p->yscroll > p->vrows - vc->vc_rows) {
1491                 p->yscroll -= p->vrows - vc->vc_rows;
1492                 fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
1493         }
1494
1495         ops->var.xoffset = 0;
1496         ops->var.yoffset = p->yscroll * vc->vc_font.height;
1497         ops->var.vmode &= ~FB_VMODE_YWRAP;
1498         ops->update_start(info);
1499         fbcon_clear_margins(vc, 1);
1500         scrollback_max += count;
1501         if (scrollback_max > scrollback_phys_max)
1502                 scrollback_max = scrollback_phys_max;
1503         scrollback_current = 0;
1504 }
1505
1506 static __inline__ void ypan_down(struct vc_data *vc, int count)
1507 {
1508         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1509         struct fbcon_display *p = &fb_display[vc->vc_num];
1510         struct fbcon_ops *ops = info->fbcon_par;
1511         
1512         p->yscroll -= count;
1513         if (p->yscroll < 0) {
1514                 ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
1515                             0, vc->vc_rows, vc->vc_cols);
1516                 p->yscroll += p->vrows - vc->vc_rows;
1517         }
1518
1519         ops->var.xoffset = 0;
1520         ops->var.yoffset = p->yscroll * vc->vc_font.height;
1521         ops->var.vmode &= ~FB_VMODE_YWRAP;
1522         ops->update_start(info);
1523         fbcon_clear_margins(vc, 1);
1524         scrollback_max -= count;
1525         if (scrollback_max < 0)
1526                 scrollback_max = 0;
1527         scrollback_current = 0;
1528 }
1529
1530 static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
1531 {
1532         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1533         struct fbcon_ops *ops = info->fbcon_par;
1534         struct fbcon_display *p = &fb_display[vc->vc_num];
1535
1536         p->yscroll -= count;
1537
1538         if (p->yscroll < 0) {
1539                 p->yscroll += p->vrows - vc->vc_rows;
1540                 fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
1541         }
1542
1543         ops->var.xoffset = 0;
1544         ops->var.yoffset = p->yscroll * vc->vc_font.height;
1545         ops->var.vmode &= ~FB_VMODE_YWRAP;
1546         ops->update_start(info);
1547         fbcon_clear_margins(vc, 1);
1548         scrollback_max -= count;
1549         if (scrollback_max < 0)
1550                 scrollback_max = 0;
1551         scrollback_current = 0;
1552 }
1553
1554 static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
1555                               int line, int count, int dy)
1556 {
1557         unsigned short *s = (unsigned short *)
1558                 (vc->vc_origin + vc->vc_size_row * line);
1559
1560         while (count--) {
1561                 unsigned short *start = s;
1562                 unsigned short *le = advance_row(s, 1);
1563                 unsigned short c;
1564                 int x = 0;
1565                 unsigned short attr = 1;
1566
1567                 do {
1568                         c = scr_readw(s);
1569                         if (attr != (c & 0xff00)) {
1570                                 attr = c & 0xff00;
1571                                 if (s > start) {
1572                                         fbcon_putcs(vc, start, s - start,
1573                                                     dy, x);
1574                                         x += s - start;
1575                                         start = s;
1576                                 }
1577                         }
1578                         console_conditional_schedule();
1579                         s++;
1580                 } while (s < le);
1581                 if (s > start)
1582                         fbcon_putcs(vc, start, s - start, dy, x);
1583                 console_conditional_schedule();
1584                 dy++;
1585         }
1586 }
1587
1588 static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info,
1589                         struct fbcon_display *p, int line, int count, int ycount)
1590 {
1591         int offset = ycount * vc->vc_cols;
1592         unsigned short *d = (unsigned short *)
1593             (vc->vc_origin + vc->vc_size_row * line);
1594         unsigned short *s = d + offset;
1595         struct fbcon_ops *ops = info->fbcon_par;
1596
1597         while (count--) {
1598                 unsigned short *start = s;
1599                 unsigned short *le = advance_row(s, 1);
1600                 unsigned short c;
1601                 int x = 0;
1602
1603                 do {
1604                         c = scr_readw(s);
1605
1606                         if (c == scr_readw(d)) {
1607                                 if (s > start) {
1608                                         ops->bmove(vc, info, line + ycount, x,
1609                                                    line, x, 1, s-start);
1610                                         x += s - start + 1;
1611                                         start = s + 1;
1612                                 } else {
1613                                         x++;
1614                                         start++;
1615                                 }
1616                         }
1617
1618                         scr_writew(c, d);
1619                         console_conditional_schedule();
1620                         s++;
1621                         d++;
1622                 } while (s < le);
1623                 if (s > start)
1624                         ops->bmove(vc, info, line + ycount, x, line, x, 1,
1625                                    s-start);
1626                 console_conditional_schedule();
1627                 if (ycount > 0)
1628                         line++;
1629                 else {
1630                         line--;
1631                         /* NOTE: We subtract two lines from these pointers */
1632                         s -= vc->vc_size_row;
1633                         d -= vc->vc_size_row;
1634                 }
1635         }
1636 }
1637
1638 static void fbcon_redraw(struct vc_data *vc, struct fbcon_display *p,
1639                          int line, int count, int offset)
1640 {
1641         unsigned short *d = (unsigned short *)
1642             (vc->vc_origin + vc->vc_size_row * line);
1643         unsigned short *s = d + offset;
1644
1645         while (count--) {
1646                 unsigned short *start = s;
1647                 unsigned short *le = advance_row(s, 1);
1648                 unsigned short c;
1649                 int x = 0;
1650                 unsigned short attr = 1;
1651
1652                 do {
1653                         c = scr_readw(s);
1654                         if (attr != (c & 0xff00)) {
1655                                 attr = c & 0xff00;
1656                                 if (s > start) {
1657                                         fbcon_putcs(vc, start, s - start,
1658                                                     line, x);
1659                                         x += s - start;
1660                                         start = s;
1661                                 }
1662                         }
1663                         if (c == scr_readw(d)) {
1664                                 if (s > start) {
1665                                         fbcon_putcs(vc, start, s - start,
1666                                                      line, x);
1667                                         x += s - start + 1;
1668                                         start = s + 1;
1669                                 } else {
1670                                         x++;
1671                                         start++;
1672                                 }
1673                         }
1674                         scr_writew(c, d);
1675                         console_conditional_schedule();
1676                         s++;
1677                         d++;
1678                 } while (s < le);
1679                 if (s > start)
1680                         fbcon_putcs(vc, start, s - start, line, x);
1681                 console_conditional_schedule();
1682                 if (offset > 0)
1683                         line++;
1684                 else {
1685                         line--;
1686                         /* NOTE: We subtract two lines from these pointers */
1687                         s -= vc->vc_size_row;
1688                         d -= vc->vc_size_row;
1689                 }
1690         }
1691 }
1692
1693 static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
1694                 enum con_scroll dir, unsigned int count)
1695 {
1696         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1697         struct fbcon_display *p = &fb_display[vc->vc_num];
1698         int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
1699
1700         if (fbcon_is_inactive(vc, info))
1701                 return true;
1702
1703         fbcon_cursor(vc, CM_ERASE);
1704
1705         /*
1706          * ++Geert: Only use ywrap/ypan if the console is in text mode
1707          * ++Andrew: Only use ypan on hardware text mode when scrolling the
1708          *           whole screen (prevents flicker).
1709          */
1710
1711         switch (dir) {
1712         case SM_UP:
1713                 if (count > vc->vc_rows)        /* Maximum realistic size */
1714                         count = vc->vc_rows;
1715                 if (logo_shown >= 0)
1716                         goto redraw_up;
1717                 switch (p->scrollmode) {
1718                 case SCROLL_MOVE:
1719                         fbcon_redraw_blit(vc, info, p, t, b - t - count,
1720                                      count);
1721                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1722                         scr_memsetw((unsigned short *) (vc->vc_origin +
1723                                                         vc->vc_size_row *
1724                                                         (b - count)),
1725                                     vc->vc_video_erase_char,
1726                                     vc->vc_size_row * count);
1727                         return true;
1728
1729                 case SCROLL_WRAP_MOVE:
1730                         if (b - t - count > 3 * vc->vc_rows >> 2) {
1731                                 if (t > 0)
1732                                         fbcon_bmove(vc, 0, 0, count, 0, t,
1733                                                     vc->vc_cols);
1734                                 ywrap_up(vc, count);
1735                                 if (vc->vc_rows - b > 0)
1736                                         fbcon_bmove(vc, b - count, 0, b, 0,
1737                                                     vc->vc_rows - b,
1738                                                     vc->vc_cols);
1739                         } else if (info->flags & FBINFO_READS_FAST)
1740                                 fbcon_bmove(vc, t + count, 0, t, 0,
1741                                             b - t - count, vc->vc_cols);
1742                         else
1743                                 goto redraw_up;
1744                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1745                         break;
1746
1747                 case SCROLL_PAN_REDRAW:
1748                         if ((p->yscroll + count <=
1749                              2 * (p->vrows - vc->vc_rows))
1750                             && ((!scroll_partial && (b - t == vc->vc_rows))
1751                                 || (scroll_partial
1752                                     && (b - t - count >
1753                                         3 * vc->vc_rows >> 2)))) {
1754                                 if (t > 0)
1755                                         fbcon_redraw_move(vc, p, 0, t, count);
1756                                 ypan_up_redraw(vc, t, count);
1757                                 if (vc->vc_rows - b > 0)
1758                                         fbcon_redraw_move(vc, p, b,
1759                                                           vc->vc_rows - b, b);
1760                         } else
1761                                 fbcon_redraw_move(vc, p, t + count, b - t - count, t);
1762                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1763                         break;
1764
1765                 case SCROLL_PAN_MOVE:
1766                         if ((p->yscroll + count <=
1767                              2 * (p->vrows - vc->vc_rows))
1768                             && ((!scroll_partial && (b - t == vc->vc_rows))
1769                                 || (scroll_partial
1770                                     && (b - t - count >
1771                                         3 * vc->vc_rows >> 2)))) {
1772                                 if (t > 0)
1773                                         fbcon_bmove(vc, 0, 0, count, 0, t,
1774                                                     vc->vc_cols);
1775                                 ypan_up(vc, count);
1776                                 if (vc->vc_rows - b > 0)
1777                                         fbcon_bmove(vc, b - count, 0, b, 0,
1778                                                     vc->vc_rows - b,
1779                                                     vc->vc_cols);
1780                         } else if (info->flags & FBINFO_READS_FAST)
1781                                 fbcon_bmove(vc, t + count, 0, t, 0,
1782                                             b - t - count, vc->vc_cols);
1783                         else
1784                                 goto redraw_up;
1785                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1786                         break;
1787
1788                 case SCROLL_REDRAW:
1789                       redraw_up:
1790                         fbcon_redraw(vc, p, t, b - t - count,
1791                                      count * vc->vc_cols);
1792                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1793                         scr_memsetw((unsigned short *) (vc->vc_origin +
1794                                                         vc->vc_size_row *
1795                                                         (b - count)),
1796                                     vc->vc_video_erase_char,
1797                                     vc->vc_size_row * count);
1798                         return true;
1799                 }
1800                 break;
1801
1802         case SM_DOWN:
1803                 if (count > vc->vc_rows)        /* Maximum realistic size */
1804                         count = vc->vc_rows;
1805                 if (logo_shown >= 0)
1806                         goto redraw_down;
1807                 switch (p->scrollmode) {
1808                 case SCROLL_MOVE:
1809                         fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
1810                                      -count);
1811                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
1812                         scr_memsetw((unsigned short *) (vc->vc_origin +
1813                                                         vc->vc_size_row *
1814                                                         t),
1815                                     vc->vc_video_erase_char,
1816                                     vc->vc_size_row * count);
1817                         return true;
1818
1819                 case SCROLL_WRAP_MOVE:
1820                         if (b - t - count > 3 * vc->vc_rows >> 2) {
1821                                 if (vc->vc_rows - b > 0)
1822                                         fbcon_bmove(vc, b, 0, b - count, 0,
1823                                                     vc->vc_rows - b,
1824                                                     vc->vc_cols);
1825                                 ywrap_down(vc, count);
1826                                 if (t > 0)
1827                                         fbcon_bmove(vc, count, 0, 0, 0, t,
1828                                                     vc->vc_cols);
1829                         } else if (info->flags & FBINFO_READS_FAST)
1830                                 fbcon_bmove(vc, t, 0, t + count, 0,
1831                                             b - t - count, vc->vc_cols);
1832                         else
1833                                 goto redraw_down;
1834                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
1835                         break;
1836
1837                 case SCROLL_PAN_MOVE:
1838                         if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1839                             && ((!scroll_partial && (b - t == vc->vc_rows))
1840                                 || (scroll_partial
1841                                     && (b - t - count >
1842                                         3 * vc->vc_rows >> 2)))) {
1843                                 if (vc->vc_rows - b > 0)
1844                                         fbcon_bmove(vc, b, 0, b - count, 0,
1845                                                     vc->vc_rows - b,
1846                                                     vc->vc_cols);
1847                                 ypan_down(vc, count);
1848                                 if (t > 0)
1849                                         fbcon_bmove(vc, count, 0, 0, 0, t,
1850                                                     vc->vc_cols);
1851                         } else if (info->flags & FBINFO_READS_FAST)
1852                                 fbcon_bmove(vc, t, 0, t + count, 0,
1853                                             b - t - count, vc->vc_cols);
1854                         else
1855                                 goto redraw_down;
1856                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
1857                         break;
1858
1859                 case SCROLL_PAN_REDRAW:
1860                         if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1861                             && ((!scroll_partial && (b - t == vc->vc_rows))
1862                                 || (scroll_partial
1863                                     && (b - t - count >
1864                                         3 * vc->vc_rows >> 2)))) {
1865                                 if (vc->vc_rows - b > 0)
1866                                         fbcon_redraw_move(vc, p, b, vc->vc_rows - b,
1867                                                           b - count);
1868                                 ypan_down_redraw(vc, t, count);
1869                                 if (t > 0)
1870                                         fbcon_redraw_move(vc, p, count, t, 0);
1871                         } else
1872                                 fbcon_redraw_move(vc, p, t, b - t - count, t + count);
1873                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
1874                         break;
1875
1876                 case SCROLL_REDRAW:
1877                       redraw_down:
1878                         fbcon_redraw(vc, p, b - 1, b - t - count,
1879                                      -count * vc->vc_cols);
1880                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
1881                         scr_memsetw((unsigned short *) (vc->vc_origin +
1882                                                         vc->vc_size_row *
1883                                                         t),
1884                                     vc->vc_video_erase_char,
1885                                     vc->vc_size_row * count);
1886                         return true;
1887                 }
1888         }
1889         return false;
1890 }
1891
1892
1893 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
1894                         int height, int width)
1895 {
1896         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1897         struct fbcon_display *p = &fb_display[vc->vc_num];
1898         
1899         if (fbcon_is_inactive(vc, info))
1900                 return;
1901
1902         if (!width || !height)
1903                 return;
1904
1905         /*  Split blits that cross physical y_wrap case.
1906          *  Pathological case involves 4 blits, better to use recursive
1907          *  code rather than unrolled case
1908          *
1909          *  Recursive invocations don't need to erase the cursor over and
1910          *  over again, so we use fbcon_bmove_rec()
1911          */
1912         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
1913                         p->vrows - p->yscroll);
1914 }
1915
1916 static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx,
1917                             int dy, int dx, int height, int width, u_int y_break)
1918 {
1919         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1920         struct fbcon_ops *ops = info->fbcon_par;
1921         u_int b;
1922
1923         if (sy < y_break && sy + height > y_break) {
1924                 b = y_break - sy;
1925                 if (dy < sy) {  /* Avoid trashing self */
1926                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1927                                         y_break);
1928                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1929                                         height - b, width, y_break);
1930                 } else {
1931                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1932                                         height - b, width, y_break);
1933                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1934                                         y_break);
1935                 }
1936                 return;
1937         }
1938
1939         if (dy < y_break && dy + height > y_break) {
1940                 b = y_break - dy;
1941                 if (dy < sy) {  /* Avoid trashing self */
1942                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1943                                         y_break);
1944                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1945                                         height - b, width, y_break);
1946                 } else {
1947                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1948                                         height - b, width, y_break);
1949                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1950                                         y_break);
1951                 }
1952                 return;
1953         }
1954         ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
1955                    height, width);
1956 }
1957
1958 static void updatescrollmode(struct fbcon_display *p,
1959                                         struct fb_info *info,
1960                                         struct vc_data *vc)
1961 {
1962         struct fbcon_ops *ops = info->fbcon_par;
1963         int fh = vc->vc_font.height;
1964         int cap = info->flags;
1965         u16 t = 0;
1966         int ypan = FBCON_SWAP(ops->rotate, info->fix.ypanstep,
1967                                   info->fix.xpanstep);
1968         int ywrap = FBCON_SWAP(ops->rotate, info->fix.ywrapstep, t);
1969         int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1970         int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
1971                                    info->var.xres_virtual);
1972         int good_pan = (cap & FBINFO_HWACCEL_YPAN) &&
1973                 divides(ypan, vc->vc_font.height) && vyres > yres;
1974         int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) &&
1975                 divides(ywrap, vc->vc_font.height) &&
1976                 divides(vc->vc_font.height, vyres) &&
1977                 divides(vc->vc_font.height, yres);
1978         int reading_fast = cap & FBINFO_READS_FAST;
1979         int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) &&
1980                 !(cap & FBINFO_HWACCEL_DISABLED);
1981         int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) &&
1982                 !(cap & FBINFO_HWACCEL_DISABLED);
1983
1984         p->vrows = vyres/fh;
1985         if (yres > (fh * (vc->vc_rows + 1)))
1986                 p->vrows -= (yres - (fh * vc->vc_rows)) / fh;
1987         if ((yres % fh) && (vyres % fh < yres % fh))
1988                 p->vrows--;
1989
1990         if (good_wrap || good_pan) {
1991                 if (reading_fast || fast_copyarea)
1992                         p->scrollmode = good_wrap ?
1993                                 SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
1994                 else
1995                         p->scrollmode = good_wrap ? SCROLL_REDRAW :
1996                                 SCROLL_PAN_REDRAW;
1997         } else {
1998                 if (reading_fast || (fast_copyarea && !fast_imageblit))
1999                         p->scrollmode = SCROLL_MOVE;
2000                 else
2001                         p->scrollmode = SCROLL_REDRAW;
2002         }
2003 }
2004
2005 #define PITCH(w) (((w) + 7) >> 3)
2006 #define CALC_FONTSZ(h, p, c) ((h) * (p) * (c)) /* size = height * pitch * charcount */
2007
2008 static int fbcon_resize(struct vc_data *vc, unsigned int width, 
2009                         unsigned int height, unsigned int user)
2010 {
2011         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2012         struct fbcon_ops *ops = info->fbcon_par;
2013         struct fbcon_display *p = &fb_display[vc->vc_num];
2014         struct fb_var_screeninfo var = info->var;
2015         int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
2016
2017         if (p->userfont && FNTSIZE(vc->vc_font.data)) {
2018                 int size;
2019                 int pitch = PITCH(vc->vc_font.width);
2020
2021                 /*
2022                  * If user font, ensure that a possible change to user font
2023                  * height or width will not allow a font data out-of-bounds access.
2024                  * NOTE: must use original charcount in calculation as font
2025                  * charcount can change and cannot be used to determine the
2026                  * font data allocated size.
2027                  */
2028                 if (pitch <= 0)
2029                         return -EINVAL;
2030                 size = CALC_FONTSZ(vc->vc_font.height, pitch, FNTCHARCNT(vc->vc_font.data));
2031                 if (size > FNTSIZE(vc->vc_font.data))
2032                         return -EINVAL;
2033         }
2034
2035         virt_w = FBCON_SWAP(ops->rotate, width, height);
2036         virt_h = FBCON_SWAP(ops->rotate, height, width);
2037         virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width,
2038                                  vc->vc_font.height);
2039         virt_fh = FBCON_SWAP(ops->rotate, vc->vc_font.height,
2040                                  vc->vc_font.width);
2041         var.xres = virt_w * virt_fw;
2042         var.yres = virt_h * virt_fh;
2043         x_diff = info->var.xres - var.xres;
2044         y_diff = info->var.yres - var.yres;
2045         if (x_diff < 0 || x_diff > virt_fw ||
2046             y_diff < 0 || y_diff > virt_fh) {
2047                 const struct fb_videomode *mode;
2048
2049                 DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
2050                 mode = fb_find_best_mode(&var, &info->modelist);
2051                 if (mode == NULL)
2052                         return -EINVAL;
2053                 display_to_var(&var, p);
2054                 fb_videomode_to_var(&var, mode);
2055
2056                 if (virt_w > var.xres/virt_fw || virt_h > var.yres/virt_fh)
2057                         return -EINVAL;
2058
2059                 DPRINTK("resize now %ix%i\n", var.xres, var.yres);
2060                 if (con_is_visible(vc)) {
2061                         var.activate = FB_ACTIVATE_NOW |
2062                                 FB_ACTIVATE_FORCE;
2063                         fb_set_var(info, &var);
2064                 }
2065                 var_to_display(p, &info->var, info);
2066                 ops->var = info->var;
2067         }
2068         updatescrollmode(p, info, vc);
2069         return 0;
2070 }
2071
2072 static int fbcon_switch(struct vc_data *vc)
2073 {
2074         struct fb_info *info, *old_info = NULL;
2075         struct fbcon_ops *ops;
2076         struct fbcon_display *p = &fb_display[vc->vc_num];
2077         struct fb_var_screeninfo var;
2078         int i, ret, prev_console, charcnt = 256;
2079
2080         info = registered_fb[con2fb_map[vc->vc_num]];
2081         ops = info->fbcon_par;
2082
2083         if (logo_shown >= 0) {
2084                 struct vc_data *conp2 = vc_cons[logo_shown].d;
2085
2086                 if (conp2->vc_top == logo_lines
2087                     && conp2->vc_bottom == conp2->vc_rows)
2088                         conp2->vc_top = 0;
2089                 logo_shown = FBCON_LOGO_CANSHOW;
2090         }
2091
2092         prev_console = ops->currcon;
2093         if (prev_console != -1)
2094                 old_info = registered_fb[con2fb_map[prev_console]];
2095         /*
2096          * FIXME: If we have multiple fbdev's loaded, we need to
2097          * update all info->currcon.  Perhaps, we can place this
2098          * in a centralized structure, but this might break some
2099          * drivers.
2100          *
2101          * info->currcon = vc->vc_num;
2102          */
2103         for_each_registered_fb(i) {
2104                 if (registered_fb[i]->fbcon_par) {
2105                         struct fbcon_ops *o = registered_fb[i]->fbcon_par;
2106
2107                         o->currcon = vc->vc_num;
2108                 }
2109         }
2110         memset(&var, 0, sizeof(struct fb_var_screeninfo));
2111         display_to_var(&var, p);
2112         var.activate = FB_ACTIVATE_NOW;
2113
2114         /*
2115          * make sure we don't unnecessarily trip the memcmp()
2116          * in fb_set_var()
2117          */
2118         info->var.activate = var.activate;
2119         var.vmode |= info->var.vmode & ~FB_VMODE_MASK;
2120         fb_set_var(info, &var);
2121         ops->var = info->var;
2122
2123         if (old_info != NULL && (old_info != info ||
2124                                  info->flags & FBINFO_MISC_ALWAYS_SETPAR)) {
2125                 if (info->fbops->fb_set_par) {
2126                         ret = info->fbops->fb_set_par(info);
2127
2128                         if (ret)
2129                                 printk(KERN_ERR "fbcon_switch: detected "
2130                                         "unhandled fb_set_par error, "
2131                                         "error code %d\n", ret);
2132                 }
2133
2134                 if (old_info != info)
2135                         fbcon_del_cursor_timer(old_info);
2136         }
2137
2138         if (fbcon_is_inactive(vc, info) ||
2139             ops->blank_state != FB_BLANK_UNBLANK)
2140                 fbcon_del_cursor_timer(info);
2141         else
2142                 fbcon_add_cursor_timer(info);
2143
2144         set_blitting_type(vc, info);
2145         ops->cursor_reset = 1;
2146
2147         if (ops->rotate_font && ops->rotate_font(info, vc)) {
2148                 ops->rotate = FB_ROTATE_UR;
2149                 set_blitting_type(vc, info);
2150         }
2151
2152         vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
2153         vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
2154
2155         if (p->userfont)
2156                 charcnt = FNTCHARCNT(vc->vc_font.data);
2157
2158         if (charcnt > 256)
2159                 vc->vc_complement_mask <<= 1;
2160
2161         updatescrollmode(p, info, vc);
2162
2163         switch (p->scrollmode) {
2164         case SCROLL_WRAP_MOVE:
2165                 scrollback_phys_max = p->vrows - vc->vc_rows;
2166                 break;
2167         case SCROLL_PAN_MOVE:
2168         case SCROLL_PAN_REDRAW:
2169                 scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
2170                 if (scrollback_phys_max < 0)
2171                         scrollback_phys_max = 0;
2172                 break;
2173         default:
2174                 scrollback_phys_max = 0;
2175                 break;
2176         }
2177
2178         scrollback_max = 0;
2179         scrollback_current = 0;
2180
2181         if (!fbcon_is_inactive(vc, info)) {
2182             ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
2183             ops->update_start(info);
2184         }
2185
2186         fbcon_set_palette(vc, color_table);     
2187         fbcon_clear_margins(vc, 0);
2188
2189         if (logo_shown == FBCON_LOGO_DRAW) {
2190
2191                 logo_shown = fg_console;
2192                 /* This is protected above by initmem_freed */
2193                 fb_show_logo(info, ops->rotate);
2194                 update_region(vc,
2195                               vc->vc_origin + vc->vc_size_row * vc->vc_top,
2196                               vc->vc_size_row * (vc->vc_bottom -
2197                                                  vc->vc_top) / 2);
2198                 return 0;
2199         }
2200         return 1;
2201 }
2202
2203 static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
2204                                 int blank)
2205 {
2206         if (blank) {
2207                 unsigned short charmask = vc->vc_hi_font_mask ?
2208                         0x1ff : 0xff;
2209                 unsigned short oldc;
2210
2211                 oldc = vc->vc_video_erase_char;
2212                 vc->vc_video_erase_char &= charmask;
2213                 fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols);
2214                 vc->vc_video_erase_char = oldc;
2215         }
2216 }
2217
2218 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
2219 {
2220         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2221         struct fbcon_ops *ops = info->fbcon_par;
2222
2223         if (mode_switch) {
2224                 struct fb_var_screeninfo var = info->var;
2225
2226                 ops->graphics = 1;
2227
2228                 if (!blank) {
2229                         var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE |
2230                                 FB_ACTIVATE_KD_TEXT;
2231                         fb_set_var(info, &var);
2232                         ops->graphics = 0;
2233                         ops->var = info->var;
2234                 }
2235         }
2236
2237         if (!fbcon_is_inactive(vc, info)) {
2238                 if (ops->blank_state != blank) {
2239                         ops->blank_state = blank;
2240                         fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
2241                         ops->cursor_flash = (!blank);
2242
2243                         if (fb_blank(info, blank))
2244                                 fbcon_generic_blank(vc, info, blank);
2245                 }
2246
2247                 if (!blank)
2248                         update_screen(vc);
2249         }
2250
2251         if (mode_switch || fbcon_is_inactive(vc, info) ||
2252             ops->blank_state != FB_BLANK_UNBLANK)
2253                 fbcon_del_cursor_timer(info);
2254         else
2255                 fbcon_add_cursor_timer(info);
2256
2257         return 0;
2258 }
2259
2260 static int fbcon_debug_enter(struct vc_data *vc)
2261 {
2262         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2263         struct fbcon_ops *ops = info->fbcon_par;
2264
2265         ops->save_graphics = ops->graphics;
2266         ops->graphics = 0;
2267         if (info->fbops->fb_debug_enter)
2268                 info->fbops->fb_debug_enter(info);
2269         fbcon_set_palette(vc, color_table);
2270         return 0;
2271 }
2272
2273 static int fbcon_debug_leave(struct vc_data *vc)
2274 {
2275         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2276         struct fbcon_ops *ops = info->fbcon_par;
2277
2278         ops->graphics = ops->save_graphics;
2279         if (info->fbops->fb_debug_leave)
2280                 info->fbops->fb_debug_leave(info);
2281         return 0;
2282 }
2283
2284 static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
2285 {
2286         u8 *fontdata = vc->vc_font.data;
2287         u8 *data = font->data;
2288         int i, j;
2289
2290         font->width = vc->vc_font.width;
2291         font->height = vc->vc_font.height;
2292         font->charcount = vc->vc_hi_font_mask ? 512 : 256;
2293         if (!font->data)
2294                 return 0;
2295
2296         if (font->width <= 8) {
2297                 j = vc->vc_font.height;
2298                 if (font->charcount * j > FNTSIZE(fontdata))
2299                         return -EINVAL;
2300
2301                 for (i = 0; i < font->charcount; i++) {
2302                         memcpy(data, fontdata, j);
2303                         memset(data + j, 0, 32 - j);
2304                         data += 32;
2305                         fontdata += j;
2306                 }
2307         } else if (font->width <= 16) {
2308                 j = vc->vc_font.height * 2;
2309                 if (font->charcount * j > FNTSIZE(fontdata))
2310                         return -EINVAL;
2311
2312                 for (i = 0; i < font->charcount; i++) {
2313                         memcpy(data, fontdata, j);
2314                         memset(data + j, 0, 64 - j);
2315                         data += 64;
2316                         fontdata += j;
2317                 }
2318         } else if (font->width <= 24) {
2319                 if (font->charcount * (vc->vc_font.height * sizeof(u32)) > FNTSIZE(fontdata))
2320                         return -EINVAL;
2321
2322                 for (i = 0; i < font->charcount; i++) {
2323                         for (j = 0; j < vc->vc_font.height; j++) {
2324                                 *data++ = fontdata[0];
2325                                 *data++ = fontdata[1];
2326                                 *data++ = fontdata[2];
2327                                 fontdata += sizeof(u32);
2328                         }
2329                         memset(data, 0, 3 * (32 - j));
2330                         data += 3 * (32 - j);
2331                 }
2332         } else {
2333                 j = vc->vc_font.height * 4;
2334                 if (font->charcount * j > FNTSIZE(fontdata))
2335                         return -EINVAL;
2336
2337                 for (i = 0; i < font->charcount; i++) {
2338                         memcpy(data, fontdata, j);
2339                         memset(data + j, 0, 128 - j);
2340                         data += 128;
2341                         fontdata += j;
2342                 }
2343         }
2344         return 0;
2345 }
2346
2347 /* set/clear vc_hi_font_mask and update vc attrs accordingly */
2348 static void set_vc_hi_font(struct vc_data *vc, bool set)
2349 {
2350         if (!set) {
2351                 vc->vc_hi_font_mask = 0;
2352                 if (vc->vc_can_do_color) {
2353                         vc->vc_complement_mask >>= 1;
2354                         vc->vc_s_complement_mask >>= 1;
2355                 }
2356                         
2357                 /* ++Edmund: reorder the attribute bits */
2358                 if (vc->vc_can_do_color) {
2359                         unsigned short *cp =
2360                             (unsigned short *) vc->vc_origin;
2361                         int count = vc->vc_screenbuf_size / 2;
2362                         unsigned short c;
2363                         for (; count > 0; count--, cp++) {
2364                                 c = scr_readw(cp);
2365                                 scr_writew(((c & 0xfe00) >> 1) |
2366                                            (c & 0xff), cp);
2367                         }
2368                         c = vc->vc_video_erase_char;
2369                         vc->vc_video_erase_char =
2370                             ((c & 0xfe00) >> 1) | (c & 0xff);
2371                         vc->vc_attr >>= 1;
2372                 }
2373         } else {
2374                 vc->vc_hi_font_mask = 0x100;
2375                 if (vc->vc_can_do_color) {
2376                         vc->vc_complement_mask <<= 1;
2377                         vc->vc_s_complement_mask <<= 1;
2378                 }
2379                         
2380                 /* ++Edmund: reorder the attribute bits */
2381                 {
2382                         unsigned short *cp =
2383                             (unsigned short *) vc->vc_origin;
2384                         int count = vc->vc_screenbuf_size / 2;
2385                         unsigned short c;
2386                         for (; count > 0; count--, cp++) {
2387                                 unsigned short newc;
2388                                 c = scr_readw(cp);
2389                                 if (vc->vc_can_do_color)
2390                                         newc =
2391                                             ((c & 0xff00) << 1) | (c &
2392                                                                    0xff);
2393                                 else
2394                                         newc = c & ~0x100;
2395                                 scr_writew(newc, cp);
2396                         }
2397                         c = vc->vc_video_erase_char;
2398                         if (vc->vc_can_do_color) {
2399                                 vc->vc_video_erase_char =
2400                                     ((c & 0xff00) << 1) | (c & 0xff);
2401                                 vc->vc_attr <<= 1;
2402                         } else
2403                                 vc->vc_video_erase_char = c & ~0x100;
2404                 }
2405         }
2406 }
2407
2408 static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
2409                              const u8 * data, int userfont)
2410 {
2411         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2412         struct fbcon_ops *ops = info->fbcon_par;
2413         struct fbcon_display *p = &fb_display[vc->vc_num];
2414         int resize;
2415         int cnt;
2416         char *old_data = NULL;
2417
2418         resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
2419         if (p->userfont)
2420                 old_data = vc->vc_font.data;
2421         if (userfont)
2422                 cnt = FNTCHARCNT(data);
2423         else
2424                 cnt = 256;
2425         vc->vc_font.data = (void *)(p->fontdata = data);
2426         if ((p->userfont = userfont))
2427                 REFCOUNT(data)++;
2428         vc->vc_font.width = w;
2429         vc->vc_font.height = h;
2430         if (vc->vc_hi_font_mask && cnt == 256)
2431                 set_vc_hi_font(vc, false);
2432         else if (!vc->vc_hi_font_mask && cnt == 512)
2433                 set_vc_hi_font(vc, true);
2434
2435         if (resize) {
2436                 int cols, rows;
2437
2438                 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2439                 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2440                 cols /= w;
2441                 rows /= h;
2442                 vc_resize(vc, cols, rows);
2443         } else if (con_is_visible(vc)
2444                    && vc->vc_mode == KD_TEXT) {
2445                 fbcon_clear_margins(vc, 0);
2446                 update_screen(vc);
2447         }
2448
2449         if (old_data && (--REFCOUNT(old_data) == 0))
2450                 kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
2451         return 0;
2452 }
2453
2454 static int fbcon_copy_font(struct vc_data *vc, int con)
2455 {
2456         struct fbcon_display *od = &fb_display[con];
2457         struct console_font *f = &vc->vc_font;
2458
2459         if (od->fontdata == f->data)
2460                 return 0;       /* already the same font... */
2461         return fbcon_do_set_font(vc, f->width, f->height, od->fontdata, od->userfont);
2462 }
2463
2464 /*
2465  *  User asked to set font; we are guaranteed that
2466  *      a) width and height are in range 1..32
2467  *      b) charcount does not exceed 512
2468  *  but lets not assume that, since someone might someday want to use larger
2469  *  fonts. And charcount of 512 is small for unicode support.
2470  *
2471  *  However, user space gives the font in 32 rows , regardless of
2472  *  actual font height. So a new API is needed if support for larger fonts
2473  *  is ever implemented.
2474  */
2475
2476 static int fbcon_set_font(struct vc_data *vc, struct console_font *font,
2477                           unsigned int flags)
2478 {
2479         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2480         unsigned charcount = font->charcount;
2481         int w = font->width;
2482         int h = font->height;
2483         int size;
2484         int i, csum;
2485         u8 *new_data, *data = font->data;
2486         int pitch = PITCH(font->width);
2487
2488         /* Is there a reason why fbconsole couldn't handle any charcount >256?
2489          * If not this check should be changed to charcount < 256 */
2490         if (charcount != 256 && charcount != 512)
2491                 return -EINVAL;
2492
2493         /* Make sure drawing engine can handle the font */
2494         if (!(info->pixmap.blit_x & (1 << (font->width - 1))) ||
2495             !(info->pixmap.blit_y & (1 << (font->height - 1))))
2496                 return -EINVAL;
2497
2498         /* Make sure driver can handle the font length */
2499         if (fbcon_invalid_charcount(info, charcount))
2500                 return -EINVAL;
2501
2502         size = CALC_FONTSZ(h, pitch, charcount);
2503
2504         new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER);
2505
2506         if (!new_data)
2507                 return -ENOMEM;
2508
2509         new_data += FONT_EXTRA_WORDS * sizeof(int);
2510         FNTSIZE(new_data) = size;
2511         FNTCHARCNT(new_data) = charcount;
2512         REFCOUNT(new_data) = 0; /* usage counter */
2513         for (i=0; i< charcount; i++) {
2514                 memcpy(new_data + i*h*pitch, data +  i*32*pitch, h*pitch);
2515         }
2516
2517         /* Since linux has a nice crc32 function use it for counting font
2518          * checksums. */
2519         csum = crc32(0, new_data, size);
2520
2521         FNTSUM(new_data) = csum;
2522         /* Check if the same font is on some other console already */
2523         for (i = first_fb_vc; i <= last_fb_vc; i++) {
2524                 struct vc_data *tmp = vc_cons[i].d;
2525                 
2526                 if (fb_display[i].userfont &&
2527                     fb_display[i].fontdata &&
2528                     FNTSUM(fb_display[i].fontdata) == csum &&
2529                     FNTSIZE(fb_display[i].fontdata) == size &&
2530                     tmp->vc_font.width == w &&
2531                     !memcmp(fb_display[i].fontdata, new_data, size)) {
2532                         kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
2533                         new_data = (u8 *)fb_display[i].fontdata;
2534                         break;
2535                 }
2536         }
2537         return fbcon_do_set_font(vc, font->width, font->height, new_data, 1);
2538 }
2539
2540 static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name)
2541 {
2542         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2543         const struct font_desc *f;
2544
2545         if (!name)
2546                 f = get_default_font(info->var.xres, info->var.yres,
2547                                      info->pixmap.blit_x, info->pixmap.blit_y);
2548         else if (!(f = find_font(name)))
2549                 return -ENOENT;
2550
2551         font->width = f->width;
2552         font->height = f->height;
2553         return fbcon_do_set_font(vc, f->width, f->height, f->data, 0);
2554 }
2555
2556 static u16 palette_red[16];
2557 static u16 palette_green[16];
2558 static u16 palette_blue[16];
2559
2560 static struct fb_cmap palette_cmap = {
2561         0, 16, palette_red, palette_green, palette_blue, NULL
2562 };
2563
2564 static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table)
2565 {
2566         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2567         int i, j, k, depth;
2568         u8 val;
2569
2570         if (fbcon_is_inactive(vc, info))
2571                 return;
2572
2573         if (!con_is_visible(vc))
2574                 return;
2575
2576         depth = fb_get_color_depth(&info->var, &info->fix);
2577         if (depth > 3) {
2578                 for (i = j = 0; i < 16; i++) {
2579                         k = table[i];
2580                         val = vc->vc_palette[j++];
2581                         palette_red[k] = (val << 8) | val;
2582                         val = vc->vc_palette[j++];
2583                         palette_green[k] = (val << 8) | val;
2584                         val = vc->vc_palette[j++];
2585                         palette_blue[k] = (val << 8) | val;
2586                 }
2587                 palette_cmap.len = 16;
2588                 palette_cmap.start = 0;
2589         /*
2590          * If framebuffer is capable of less than 16 colors,
2591          * use default palette of fbcon.
2592          */
2593         } else
2594                 fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap);
2595
2596         fb_set_cmap(&palette_cmap, info);
2597 }
2598
2599 static u16 *fbcon_screen_pos(const struct vc_data *vc, int offset)
2600 {
2601         return (u16 *) (vc->vc_origin + offset);
2602 }
2603
2604 static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
2605                                  int *px, int *py)
2606 {
2607         unsigned long ret;
2608         int x, y;
2609
2610         if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
2611                 unsigned long offset = (pos - vc->vc_origin) / 2;
2612
2613                 x = offset % vc->vc_cols;
2614                 y = offset / vc->vc_cols;
2615                 ret = pos + (vc->vc_cols - x) * 2;
2616         } else {
2617                 /* Should not happen */
2618                 x = y = 0;
2619                 ret = vc->vc_origin;
2620         }
2621         if (px)
2622                 *px = x;
2623         if (py)
2624                 *py = y;
2625         return ret;
2626 }
2627
2628 /* As we might be inside of softback, we may work with non-contiguous buffer,
2629    that's why we have to use a separate routine. */
2630 static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
2631 {
2632         while (cnt--) {
2633                 u16 a = scr_readw(p);
2634                 if (!vc->vc_can_do_color)
2635                         a ^= 0x0800;
2636                 else if (vc->vc_hi_font_mask == 0x100)
2637                         a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
2638                             (((a) & 0x0e00) << 4);
2639                 else
2640                         a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
2641                             (((a) & 0x0700) << 4);
2642                 scr_writew(a, p++);
2643         }
2644 }
2645
2646 void fbcon_suspended(struct fb_info *info)
2647 {
2648         struct vc_data *vc = NULL;
2649         struct fbcon_ops *ops = info->fbcon_par;
2650
2651         if (!ops || ops->currcon < 0)
2652                 return;
2653         vc = vc_cons[ops->currcon].d;
2654
2655         /* Clear cursor, restore saved data */
2656         fbcon_cursor(vc, CM_ERASE);
2657 }
2658
2659 void fbcon_resumed(struct fb_info *info)
2660 {
2661         struct vc_data *vc;
2662         struct fbcon_ops *ops = info->fbcon_par;
2663
2664         if (!ops || ops->currcon < 0)
2665                 return;
2666         vc = vc_cons[ops->currcon].d;
2667
2668         update_screen(vc);
2669 }
2670
2671 static void fbcon_modechanged(struct fb_info *info)
2672 {
2673         struct fbcon_ops *ops = info->fbcon_par;
2674         struct vc_data *vc;
2675         struct fbcon_display *p;
2676         int rows, cols;
2677
2678         if (!ops || ops->currcon < 0)
2679                 return;
2680         vc = vc_cons[ops->currcon].d;
2681         if (vc->vc_mode != KD_TEXT ||
2682             registered_fb[con2fb_map[ops->currcon]] != info)
2683                 return;
2684
2685         p = &fb_display[vc->vc_num];
2686         set_blitting_type(vc, info);
2687
2688         if (con_is_visible(vc)) {
2689                 var_to_display(p, &info->var, info);
2690                 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2691                 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2692                 cols /= vc->vc_font.width;
2693                 rows /= vc->vc_font.height;
2694                 vc_resize(vc, cols, rows);
2695                 updatescrollmode(p, info, vc);
2696                 scrollback_max = 0;
2697                 scrollback_current = 0;
2698
2699                 if (!fbcon_is_inactive(vc, info)) {
2700                     ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
2701                     ops->update_start(info);
2702                 }
2703
2704                 fbcon_set_palette(vc, color_table);
2705                 update_screen(vc);
2706         }
2707 }
2708
2709 static void fbcon_set_all_vcs(struct fb_info *info)
2710 {
2711         struct fbcon_ops *ops = info->fbcon_par;
2712         struct vc_data *vc;
2713         struct fbcon_display *p;
2714         int i, rows, cols, fg = -1;
2715
2716         if (!ops || ops->currcon < 0)
2717                 return;
2718
2719         for (i = first_fb_vc; i <= last_fb_vc; i++) {
2720                 vc = vc_cons[i].d;
2721                 if (!vc || vc->vc_mode != KD_TEXT ||
2722                     registered_fb[con2fb_map[i]] != info)
2723                         continue;
2724
2725                 if (con_is_visible(vc)) {
2726                         fg = i;
2727                         continue;
2728                 }
2729
2730                 p = &fb_display[vc->vc_num];
2731                 set_blitting_type(vc, info);
2732                 var_to_display(p, &info->var, info);
2733                 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2734                 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2735                 cols /= vc->vc_font.width;
2736                 rows /= vc->vc_font.height;
2737                 vc_resize(vc, cols, rows);
2738         }
2739
2740         if (fg != -1)
2741                 fbcon_modechanged(info);
2742 }
2743
2744
2745 void fbcon_update_vcs(struct fb_info *info, bool all)
2746 {
2747         if (all)
2748                 fbcon_set_all_vcs(info);
2749         else
2750                 fbcon_modechanged(info);
2751 }
2752 EXPORT_SYMBOL(fbcon_update_vcs);
2753
2754 int fbcon_mode_deleted(struct fb_info *info,
2755                        struct fb_videomode *mode)
2756 {
2757         struct fb_info *fb_info;
2758         struct fbcon_display *p;
2759         int i, j, found = 0;
2760
2761         /* before deletion, ensure that mode is not in use */
2762         for (i = first_fb_vc; i <= last_fb_vc; i++) {
2763                 j = con2fb_map[i];
2764                 if (j == -1)
2765                         continue;
2766                 fb_info = registered_fb[j];
2767                 if (fb_info != info)
2768                         continue;
2769                 p = &fb_display[i];
2770                 if (!p || !p->mode)
2771                         continue;
2772                 if (fb_mode_is_equal(p->mode, mode)) {
2773                         found = 1;
2774                         break;
2775                 }
2776         }
2777         return found;
2778 }
2779
2780 #ifdef CONFIG_VT_HW_CONSOLE_BINDING
2781 static void fbcon_unbind(void)
2782 {
2783         int ret;
2784
2785         ret = do_unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc,
2786                                 fbcon_is_default);
2787
2788         if (!ret)
2789                 fbcon_has_console_bind = 0;
2790 }
2791 #else
2792 static inline void fbcon_unbind(void) {}
2793 #endif /* CONFIG_VT_HW_CONSOLE_BINDING */
2794
2795 /* called with console_lock held */
2796 void fbcon_fb_unbind(struct fb_info *info)
2797 {
2798         int i, new_idx = -1, ret = 0;
2799         int idx = info->node;
2800
2801         WARN_CONSOLE_UNLOCKED();
2802
2803         if (!fbcon_has_console_bind)
2804                 return;
2805
2806         for (i = first_fb_vc; i <= last_fb_vc; i++) {
2807                 if (con2fb_map[i] != idx &&
2808                     con2fb_map[i] != -1) {
2809                         new_idx = con2fb_map[i];
2810                         break;
2811                 }
2812         }
2813
2814         if (new_idx != -1) {
2815                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2816                         if (con2fb_map[i] == idx)
2817                                 set_con2fb_map(i, new_idx, 0);
2818                 }
2819         } else {
2820                 struct fb_info *info = registered_fb[idx];
2821
2822                 /* This is sort of like set_con2fb_map, except it maps
2823                  * the consoles to no device and then releases the
2824                  * oldinfo to free memory and cancel the cursor blink
2825                  * timer. I can imagine this just becoming part of
2826                  * set_con2fb_map where new_idx is -1
2827                  */
2828                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2829                         if (con2fb_map[i] == idx) {
2830                                 con2fb_map[i] = -1;
2831                                 if (!search_fb_in_map(idx)) {
2832                                         ret = con2fb_release_oldinfo(vc_cons[i].d,
2833                                                                      info, NULL, i,
2834                                                                      idx, 0);
2835                                         if (ret) {
2836                                                 con2fb_map[i] = idx;
2837                                                 return;
2838                                         }
2839                                 }
2840                         }
2841                 }
2842                 fbcon_unbind();
2843         }
2844 }
2845
2846 /* called with console_lock held */
2847 void fbcon_fb_unregistered(struct fb_info *info)
2848 {
2849         int i, idx;
2850
2851         WARN_CONSOLE_UNLOCKED();
2852
2853         if (deferred_takeover)
2854                 return;
2855
2856         idx = info->node;
2857         for (i = first_fb_vc; i <= last_fb_vc; i++) {
2858                 if (con2fb_map[i] == idx)
2859                         con2fb_map[i] = -1;
2860         }
2861
2862         if (idx == info_idx) {
2863                 info_idx = -1;
2864
2865                 for_each_registered_fb(i) {
2866                         info_idx = i;
2867                         break;
2868                 }
2869         }
2870
2871         if (info_idx != -1) {
2872                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2873                         if (con2fb_map[i] == -1)
2874                                 con2fb_map[i] = info_idx;
2875                 }
2876         }
2877
2878         if (primary_device == idx)
2879                 primary_device = -1;
2880
2881         if (!num_registered_fb)
2882                 do_unregister_con_driver(&fb_con);
2883 }
2884
2885 void fbcon_remap_all(struct fb_info *info)
2886 {
2887         int i, idx = info->node;
2888
2889         console_lock();
2890         if (deferred_takeover) {
2891                 for (i = first_fb_vc; i <= last_fb_vc; i++)
2892                         con2fb_map_boot[i] = idx;
2893                 fbcon_map_override();
2894                 console_unlock();
2895                 return;
2896         }
2897
2898         for (i = first_fb_vc; i <= last_fb_vc; i++)
2899                 set_con2fb_map(i, idx, 0);
2900
2901         if (con_is_bound(&fb_con)) {
2902                 printk(KERN_INFO "fbcon: Remapping primary device, "
2903                        "fb%i, to tty %i-%i\n", idx,
2904                        first_fb_vc + 1, last_fb_vc + 1);
2905                 info_idx = idx;
2906         }
2907         console_unlock();
2908 }
2909
2910 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
2911 static void fbcon_select_primary(struct fb_info *info)
2912 {
2913         if (!map_override && primary_device == -1 &&
2914             fb_is_primary_device(info)) {
2915                 int i;
2916
2917                 printk(KERN_INFO "fbcon: %s (fb%i) is primary device\n",
2918                        info->fix.id, info->node);
2919                 primary_device = info->node;
2920
2921                 for (i = first_fb_vc; i <= last_fb_vc; i++)
2922                         con2fb_map_boot[i] = primary_device;
2923
2924                 if (con_is_bound(&fb_con)) {
2925                         printk(KERN_INFO "fbcon: Remapping primary device, "
2926                                "fb%i, to tty %i-%i\n", info->node,
2927                                first_fb_vc + 1, last_fb_vc + 1);
2928                         info_idx = primary_device;
2929                 }
2930         }
2931
2932 }
2933 #else
2934 static inline void fbcon_select_primary(struct fb_info *info)
2935 {
2936         return;
2937 }
2938 #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */
2939
2940 /* called with console_lock held */
2941 int fbcon_fb_registered(struct fb_info *info)
2942 {
2943         int ret = 0, i, idx;
2944
2945         WARN_CONSOLE_UNLOCKED();
2946
2947         idx = info->node;
2948         fbcon_select_primary(info);
2949
2950         if (deferred_takeover) {
2951                 pr_info("fbcon: Deferring console take-over\n");
2952                 return 0;
2953         }
2954
2955         if (info_idx == -1) {
2956                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2957                         if (con2fb_map_boot[i] == idx) {
2958                                 info_idx = idx;
2959                                 break;
2960                         }
2961                 }
2962
2963                 if (info_idx != -1)
2964                         ret = do_fbcon_takeover(1);
2965         } else {
2966                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2967                         if (con2fb_map_boot[i] == idx)
2968                                 set_con2fb_map(i, idx, 0);
2969                 }
2970         }
2971
2972         return ret;
2973 }
2974
2975 void fbcon_fb_blanked(struct fb_info *info, int blank)
2976 {
2977         struct fbcon_ops *ops = info->fbcon_par;
2978         struct vc_data *vc;
2979
2980         if (!ops || ops->currcon < 0)
2981                 return;
2982
2983         vc = vc_cons[ops->currcon].d;
2984         if (vc->vc_mode != KD_TEXT ||
2985                         registered_fb[con2fb_map[ops->currcon]] != info)
2986                 return;
2987
2988         if (con_is_visible(vc)) {
2989                 if (blank)
2990                         do_blank_screen(0);
2991                 else
2992                         do_unblank_screen(0);
2993         }
2994         ops->blank_state = blank;
2995 }
2996
2997 void fbcon_new_modelist(struct fb_info *info)
2998 {
2999         int i;
3000         struct vc_data *vc;
3001         struct fb_var_screeninfo var;
3002         const struct fb_videomode *mode;
3003
3004         for (i = first_fb_vc; i <= last_fb_vc; i++) {
3005                 if (registered_fb[con2fb_map[i]] != info)
3006                         continue;
3007                 if (!fb_display[i].mode)
3008                         continue;
3009                 vc = vc_cons[i].d;
3010                 display_to_var(&var, &fb_display[i]);
3011                 mode = fb_find_nearest_mode(fb_display[i].mode,
3012                                             &info->modelist);
3013                 fb_videomode_to_var(&var, mode);
3014                 fbcon_set_disp(info, &var, vc->vc_num);
3015         }
3016 }
3017
3018 void fbcon_get_requirement(struct fb_info *info,
3019                            struct fb_blit_caps *caps)
3020 {
3021         struct vc_data *vc;
3022         struct fbcon_display *p;
3023
3024         if (caps->flags) {
3025                 int i, charcnt;
3026
3027                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
3028                         vc = vc_cons[i].d;
3029                         if (vc && vc->vc_mode == KD_TEXT &&
3030                             info->node == con2fb_map[i]) {
3031                                 p = &fb_display[i];
3032                                 caps->x |= 1 << (vc->vc_font.width - 1);
3033                                 caps->y |= 1 << (vc->vc_font.height - 1);
3034                                 charcnt = (p->userfont) ?
3035                                         FNTCHARCNT(p->fontdata) : 256;
3036                                 if (caps->len < charcnt)
3037                                         caps->len = charcnt;
3038                         }
3039                 }
3040         } else {
3041                 vc = vc_cons[fg_console].d;
3042
3043                 if (vc && vc->vc_mode == KD_TEXT &&
3044                     info->node == con2fb_map[fg_console]) {
3045                         p = &fb_display[fg_console];
3046                         caps->x = 1 << (vc->vc_font.width - 1);
3047                         caps->y = 1 << (vc->vc_font.height - 1);
3048                         caps->len = (p->userfont) ?
3049                                 FNTCHARCNT(p->fontdata) : 256;
3050                 }
3051         }
3052 }
3053
3054 int fbcon_set_con2fb_map_ioctl(void __user *argp)
3055 {
3056         struct fb_con2fbmap con2fb;
3057         int ret;
3058
3059         if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3060                 return -EFAULT;
3061         if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3062                 return -EINVAL;
3063         if (con2fb.framebuffer >= FB_MAX)
3064                 return -EINVAL;
3065         if (!registered_fb[con2fb.framebuffer])
3066                 request_module("fb%d", con2fb.framebuffer);
3067         if (!registered_fb[con2fb.framebuffer]) {
3068                 return -EINVAL;
3069         }
3070
3071         console_lock();
3072         ret = set_con2fb_map(con2fb.console - 1,
3073                              con2fb.framebuffer, 1);
3074         console_unlock();
3075
3076         return ret;
3077 }
3078
3079 int fbcon_get_con2fb_map_ioctl(void __user *argp)
3080 {
3081         struct fb_con2fbmap con2fb;
3082
3083         if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3084                 return -EFAULT;
3085         if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3086                 return -EINVAL;
3087
3088         console_lock();
3089         con2fb.framebuffer = con2fb_map[con2fb.console - 1];
3090         console_unlock();
3091
3092         return copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
3093 }
3094
3095 /*
3096  *  The console `switch' structure for the frame buffer based console
3097  */
3098
3099 static const struct consw fb_con = {
3100         .owner                  = THIS_MODULE,
3101         .con_startup            = fbcon_startup,
3102         .con_init               = fbcon_init,
3103         .con_deinit             = fbcon_deinit,
3104         .con_clear              = fbcon_clear,
3105         .con_putc               = fbcon_putc,
3106         .con_putcs              = fbcon_putcs,
3107         .con_cursor             = fbcon_cursor,
3108         .con_scroll             = fbcon_scroll,
3109         .con_switch             = fbcon_switch,
3110         .con_blank              = fbcon_blank,
3111         .con_font_set           = fbcon_set_font,
3112         .con_font_get           = fbcon_get_font,
3113         .con_font_default       = fbcon_set_def_font,
3114         .con_font_copy          = fbcon_copy_font,
3115         .con_set_palette        = fbcon_set_palette,
3116         .con_invert_region      = fbcon_invert_region,
3117         .con_screen_pos         = fbcon_screen_pos,
3118         .con_getxy              = fbcon_getxy,
3119         .con_resize             = fbcon_resize,
3120         .con_debug_enter        = fbcon_debug_enter,
3121         .con_debug_leave        = fbcon_debug_leave,
3122 };
3123
3124 static ssize_t store_rotate(struct device *device,
3125                             struct device_attribute *attr, const char *buf,
3126                             size_t count)
3127 {
3128         struct fb_info *info;
3129         int rotate, idx;
3130         char **last = NULL;
3131
3132         console_lock();
3133         idx = con2fb_map[fg_console];
3134
3135         if (idx == -1 || registered_fb[idx] == NULL)
3136                 goto err;
3137
3138         info = registered_fb[idx];
3139         rotate = simple_strtoul(buf, last, 0);
3140         fbcon_rotate(info, rotate);
3141 err:
3142         console_unlock();
3143         return count;
3144 }
3145
3146 static ssize_t store_rotate_all(struct device *device,
3147                                 struct device_attribute *attr,const char *buf,
3148                                 size_t count)
3149 {
3150         struct fb_info *info;
3151         int rotate, idx;
3152         char **last = NULL;
3153
3154         console_lock();
3155         idx = con2fb_map[fg_console];
3156
3157         if (idx == -1 || registered_fb[idx] == NULL)
3158                 goto err;
3159
3160         info = registered_fb[idx];
3161         rotate = simple_strtoul(buf, last, 0);
3162         fbcon_rotate_all(info, rotate);
3163 err:
3164         console_unlock();
3165         return count;
3166 }
3167
3168 static ssize_t show_rotate(struct device *device,
3169                            struct device_attribute *attr,char *buf)
3170 {
3171         struct fb_info *info;
3172         int rotate = 0, idx;
3173
3174         console_lock();
3175         idx = con2fb_map[fg_console];
3176
3177         if (idx == -1 || registered_fb[idx] == NULL)
3178                 goto err;
3179
3180         info = registered_fb[idx];
3181         rotate = fbcon_get_rotate(info);
3182 err:
3183         console_unlock();
3184         return snprintf(buf, PAGE_SIZE, "%d\n", rotate);
3185 }
3186
3187 static ssize_t show_cursor_blink(struct device *device,
3188                                  struct device_attribute *attr, char *buf)
3189 {
3190         struct fb_info *info;
3191         struct fbcon_ops *ops;
3192         int idx, blink = -1;
3193
3194         console_lock();
3195         idx = con2fb_map[fg_console];
3196
3197         if (idx == -1 || registered_fb[idx] == NULL)
3198                 goto err;
3199
3200         info = registered_fb[idx];
3201         ops = info->fbcon_par;
3202
3203         if (!ops)
3204                 goto err;
3205
3206         blink = (ops->flags & FBCON_FLAGS_CURSOR_TIMER) ? 1 : 0;
3207 err:
3208         console_unlock();
3209         return snprintf(buf, PAGE_SIZE, "%d\n", blink);
3210 }
3211
3212 static ssize_t store_cursor_blink(struct device *device,
3213                                   struct device_attribute *attr,
3214                                   const char *buf, size_t count)
3215 {
3216         struct fb_info *info;
3217         int blink, idx;
3218         char **last = NULL;
3219
3220         console_lock();
3221         idx = con2fb_map[fg_console];
3222
3223         if (idx == -1 || registered_fb[idx] == NULL)
3224                 goto err;
3225
3226         info = registered_fb[idx];
3227
3228         if (!info->fbcon_par)
3229                 goto err;
3230
3231         blink = simple_strtoul(buf, last, 0);
3232
3233         if (blink) {
3234                 fbcon_cursor_noblink = 0;
3235                 fbcon_add_cursor_timer(info);
3236         } else {
3237                 fbcon_cursor_noblink = 1;
3238                 fbcon_del_cursor_timer(info);
3239         }
3240
3241 err:
3242         console_unlock();
3243         return count;
3244 }
3245
3246 static struct device_attribute device_attrs[] = {
3247         __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
3248         __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all),
3249         __ATTR(cursor_blink, S_IRUGO|S_IWUSR, show_cursor_blink,
3250                store_cursor_blink),
3251 };
3252
3253 static int fbcon_init_device(void)
3254 {
3255         int i, error = 0;
3256
3257         fbcon_has_sysfs = 1;
3258
3259         for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
3260                 error = device_create_file(fbcon_device, &device_attrs[i]);
3261
3262                 if (error)
3263                         break;
3264         }
3265
3266         if (error) {
3267                 while (--i >= 0)
3268                         device_remove_file(fbcon_device, &device_attrs[i]);
3269
3270                 fbcon_has_sysfs = 0;
3271         }
3272
3273         return 0;
3274 }
3275
3276 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3277 static void fbcon_register_existing_fbs(struct work_struct *work)
3278 {
3279         int i;
3280
3281         console_lock();
3282
3283         for_each_registered_fb(i)
3284                 fbcon_fb_registered(registered_fb[i]);
3285
3286         console_unlock();
3287 }
3288
3289 static struct notifier_block fbcon_output_nb;
3290 static DECLARE_WORK(fbcon_deferred_takeover_work, fbcon_register_existing_fbs);
3291
3292 static int fbcon_output_notifier(struct notifier_block *nb,
3293                                  unsigned long action, void *data)
3294 {
3295         WARN_CONSOLE_UNLOCKED();
3296
3297         pr_info("fbcon: Taking over console\n");
3298
3299         dummycon_unregister_output_notifier(&fbcon_output_nb);
3300         deferred_takeover = false;
3301         logo_shown = FBCON_LOGO_DONTSHOW;
3302
3303         /* We may get called in atomic context */
3304         schedule_work(&fbcon_deferred_takeover_work);
3305
3306         return NOTIFY_OK;
3307 }
3308 #endif
3309
3310 static void fbcon_start(void)
3311 {
3312         WARN_CONSOLE_UNLOCKED();
3313
3314 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3315         if (conswitchp != &dummy_con)
3316                 deferred_takeover = false;
3317
3318         if (deferred_takeover) {
3319                 fbcon_output_nb.notifier_call = fbcon_output_notifier;
3320                 dummycon_register_output_notifier(&fbcon_output_nb);
3321                 return;
3322         }
3323 #endif
3324
3325         if (num_registered_fb) {
3326                 int i;
3327
3328                 for_each_registered_fb(i) {
3329                         info_idx = i;
3330                         break;
3331                 }
3332
3333                 do_fbcon_takeover(0);
3334         }
3335 }
3336
3337 static void fbcon_exit(void)
3338 {
3339         struct fb_info *info;
3340         int i, j, mapped;
3341
3342 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3343         if (deferred_takeover) {
3344                 dummycon_unregister_output_notifier(&fbcon_output_nb);
3345                 deferred_takeover = false;
3346         }
3347 #endif
3348
3349         for_each_registered_fb(i) {
3350                 int pending = 0;
3351
3352                 mapped = 0;
3353                 info = registered_fb[i];
3354
3355                 if (info->queue.func)
3356                         pending = cancel_work_sync(&info->queue);
3357                 DPRINTK("fbcon: %s pending work\n", (pending ? "canceled" :
3358                         "no"));
3359
3360                 for (j = first_fb_vc; j <= last_fb_vc; j++) {
3361                         if (con2fb_map[j] == i) {
3362                                 mapped = 1;
3363                                 con2fb_map[j] = -1;
3364                         }
3365                 }
3366
3367                 if (mapped) {
3368                         if (info->fbops->fb_release)
3369                                 info->fbops->fb_release(info, 0);
3370                         module_put(info->fbops->owner);
3371
3372                         if (info->fbcon_par) {
3373                                 struct fbcon_ops *ops = info->fbcon_par;
3374
3375                                 fbcon_del_cursor_timer(info);
3376                                 kfree(ops->cursor_src);
3377                                 kfree(ops->cursor_state.mask);
3378                                 kfree(info->fbcon_par);
3379                                 info->fbcon_par = NULL;
3380                         }
3381
3382                         if (info->queue.func == fb_flashcursor)
3383                                 info->queue.func = NULL;
3384                 }
3385         }
3386 }
3387
3388 void __init fb_console_init(void)
3389 {
3390         int i;
3391
3392         console_lock();
3393         fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
3394                                      "fbcon");
3395
3396         if (IS_ERR(fbcon_device)) {
3397                 printk(KERN_WARNING "Unable to create device "
3398                        "for fbcon; errno = %ld\n",
3399                        PTR_ERR(fbcon_device));
3400                 fbcon_device = NULL;
3401         } else
3402                 fbcon_init_device();
3403
3404         for (i = 0; i < MAX_NR_CONSOLES; i++)
3405                 con2fb_map[i] = -1;
3406
3407         fbcon_start();
3408         console_unlock();
3409 }
3410
3411 #ifdef MODULE
3412
3413 static void __exit fbcon_deinit_device(void)
3414 {
3415         int i;
3416
3417         if (fbcon_has_sysfs) {
3418                 for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
3419                         device_remove_file(fbcon_device, &device_attrs[i]);
3420
3421                 fbcon_has_sysfs = 0;
3422         }
3423 }
3424
3425 void __exit fb_console_exit(void)
3426 {
3427         console_lock();
3428         fbcon_deinit_device();
3429         device_destroy(fb_class, MKDEV(0, 0));
3430         fbcon_exit();
3431         do_unregister_con_driver(&fb_con);
3432         console_unlock();
3433 }       
3434 #endif