VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / drivers / video / console / 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/config.h>
62 #include <linux/module.h>
63 #include <linux/types.h>
64 #include <linux/sched.h>
65 #include <linux/fs.h>
66 #include <linux/kernel.h>
67 #include <linux/delay.h>        /* MSch: for IRQ probe */
68 #include <linux/tty.h>
69 #include <linux/console.h>
70 #include <linux/string.h>
71 #include <linux/kd.h>
72 #include <linux/slab.h>
73 #include <linux/fb.h>
74 #include <linux/vt_kern.h>
75 #include <linux/selection.h>
76 #include <linux/font.h>
77 #include <linux/smp.h>
78 #include <linux/init.h>
79 #include <linux/interrupt.h>
80
81 #include <asm/irq.h>
82 #include <asm/system.h>
83 #include <asm/uaccess.h>
84 #ifdef CONFIG_ATARI
85 #include <asm/atariints.h>
86 #endif
87 #ifdef CONFIG_MAC
88 #include <asm/macints.h>
89 #endif
90 #if defined(__mc68000__) || defined(CONFIG_APUS)
91 #include <asm/machdep.h>
92 #include <asm/setup.h>
93 #endif
94
95 #include "fbcon.h"
96
97 #ifdef FBCONDEBUG
98 #  define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
99 #else
100 #  define DPRINTK(fmt, args...)
101 #endif
102
103 struct display fb_display[MAX_NR_CONSOLES];
104 signed char con2fb_map[MAX_NR_CONSOLES];
105 static int logo_height;
106 static int logo_lines;
107 static int logo_shown = -1;
108 /* Software scrollback */
109 int fbcon_softback_size = 32768;
110 static unsigned long softback_buf, softback_curr;
111 static unsigned long softback_in;
112 static unsigned long softback_top, softback_end;
113 static int softback_lines;
114 /* console mappings */
115 static int first_fb_vc;
116 static int last_fb_vc = MAX_NR_CONSOLES - 1;
117 static int fbcon_is_default = 1; 
118 /* font data */
119 static char fontname[40];
120
121 /* current fb_info */
122 static int info_idx =  -1;
123
124 #define REFCOUNT(fd)    (((int *)(fd))[-1])
125 #define FNTSIZE(fd)     (((int *)(fd))[-2])
126 #define FNTCHARCNT(fd)  (((int *)(fd))[-3])
127 #define FNTSUM(fd)      (((int *)(fd))[-4])
128 #define FONT_EXTRA_WORDS 4
129
130 #define CM_SOFTBACK     (8)
131
132 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
133
134 static void fbcon_free_font(struct display *);
135 static int fbcon_set_origin(struct vc_data *);
136
137 #define CURSOR_DRAW_DELAY               (1)
138
139 /* # VBL ints between cursor state changes */
140 #define ARM_CURSOR_BLINK_RATE           (10)
141 #define ATARI_CURSOR_BLINK_RATE         (42)
142 #define MAC_CURSOR_BLINK_RATE           (32)
143 #define DEFAULT_CURSOR_BLINK_RATE       (20)
144
145 static int vbl_cursor_cnt;
146
147 #define divides(a, b)   ((!(a) || (b)%(a)) ? 0 : 1)
148
149 /*
150  *  Interface used by the world
151  */
152
153 static const char *fbcon_startup(void);
154 static void fbcon_init(struct vc_data *vc, int init);
155 static void fbcon_deinit(struct vc_data *vc);
156 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
157                         int width);
158 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos);
159 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
160                         int count, int ypos, int xpos);
161 static void fbcon_cursor(struct vc_data *vc, int mode);
162 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
163                         int count);
164 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
165                         int height, int width);
166 static int fbcon_switch(struct vc_data *vc);
167 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch);
168 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table);
169 static int fbcon_scrolldelta(struct vc_data *vc, int lines);
170 void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
171                          int bottom_only);
172
173
174 /*
175  *  Internal routines
176  */
177 static __inline__ int real_y(struct display *p, int ypos);
178 static __inline__ void ywrap_up(struct vc_data *vc, int count);
179 static __inline__ void ywrap_down(struct vc_data *vc, int count);
180 static __inline__ void ypan_up(struct vc_data *vc, int count);
181 static __inline__ void ypan_down(struct vc_data *vc, int count);
182 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
183                             int dy, int dx, int height, int width, u_int y_break);
184 static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc);
185 static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
186                               int line, int count, int dy);
187
188 #ifdef CONFIG_MAC
189 /*
190  * On the Macintoy, there may or may not be a working VBL int. We need to probe
191  */
192 static int vbl_detected;
193
194 static irqreturn_t fb_vbl_detect(int irq, void *dummy, struct pt_regs *fp)
195 {
196         vbl_detected++;
197         return IRQ_HANDLED;
198 }
199 #endif
200
201 static void fb_flashcursor(void *private)
202 {
203         struct fb_info *info = (struct fb_info *) private;
204         struct vc_data *vc = NULL;
205
206         if (info->currcon != -1)
207                 vc = vc_cons[info->currcon].d;
208
209         if (info->state != FBINFO_STATE_RUNNING ||
210             info->cursor.rop == ROP_COPY || !vc || !CON_IS_VISIBLE(vc)
211             || registered_fb[(int) con2fb_map[vc->vc_num]] != info)
212                 return;
213         acquire_console_sem();
214         info->cursor.enable ^= 1;
215         info->fbops->fb_cursor(info, &info->cursor);
216         release_console_sem();
217 }
218
219 #if (defined(__arm__) && defined(IRQ_VSYNCPULSE)) || defined(CONFIG_ATARI) || defined(CONFIG_MAC)
220 static int cursor_blink_rate;
221 static irqreturn_t fb_vbl_handler(int irq, void *dev_id, struct pt_regs *fp)
222 {
223         struct fb_info *info = dev_id;
224
225         if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
226                 schedule_work(&info->queue);    
227                 vbl_cursor_cnt = cursor_blink_rate; 
228         }
229         return IRQ_HANDLED;
230 }
231 #endif
232         
233 static void cursor_timer_handler(unsigned long dev_addr)
234 {
235         struct fb_info *info = (struct fb_info *) dev_addr;
236         
237         schedule_work(&info->queue);
238         mod_timer(&info->cursor_timer, jiffies + HZ/5);
239 }
240
241 int __init fb_console_setup(char *this_opt)
242 {
243         char *options;
244         int i, j;
245
246         if (!this_opt || !*this_opt)
247                 return 0;
248
249         while ((options = strsep(&this_opt, ",")) != NULL) {
250                 if (!strncmp(options, "font:", 5))
251                         strcpy(fontname, options + 5);
252                 
253                 if (!strncmp(options, "scrollback:", 11)) {
254                         options += 11;
255                         if (*options) {
256                                 fbcon_softback_size = simple_strtoul(options, &options, 0);
257                                 if (*options == 'k' || *options == 'K') {
258                                         fbcon_softback_size *= 1024;
259                                         options++;
260                                 }
261                                 if (*options != ',')
262                                         return 0;
263                                 options++;
264                         } else
265                                 return 0;
266                 }
267                 
268                 if (!strncmp(options, "map:", 4)) {
269                         options += 4;
270                         if (*options)
271                                 for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
272                                         if (!options[j])
273                                                 j = 0;
274                                         con2fb_map[i] = (options[j++]-'0') % FB_MAX;
275                                 }
276                         return 0;
277                 }
278
279                 if (!strncmp(options, "vc:", 3)) {
280                         options += 3;
281                         if (*options)
282                                 first_fb_vc = simple_strtoul(options, &options, 10) - 1;
283                         if (first_fb_vc < 0)
284                                 first_fb_vc = 0;
285                         if (*options++ == '-')
286                                 last_fb_vc = simple_strtoul(options, &options, 10) - 1;
287                         fbcon_is_default = 0; 
288                 }       
289         }
290         return 0;
291 }
292
293 __setup("fbcon=", fb_console_setup);
294
295 static int search_fb_in_map(int idx)
296 {
297         int i;
298
299         for (i = 0; i < MAX_NR_CONSOLES; i++) {
300                 if (con2fb_map[i] == idx)
301                         return 1;
302         }
303         return 0;
304 }
305
306 static int search_for_mapped_con(void)
307 {
308         int i;
309
310         for (i = 0; i < MAX_NR_CONSOLES; i++) {
311                 if (con2fb_map[i] != -1)
312                         return 1;
313         }
314         return 0;
315 }
316
317 /**
318  *      set_con2fb_map - map console to frame buffer device
319  *      @unit: virtual console number to map
320  *      @newidx: frame buffer index to map virtual console to
321  *
322  *      Maps a virtual console @unit to a frame buffer device
323  *      @newidx.
324  */
325 int set_con2fb_map(int unit, int newidx)
326 {
327         struct vc_data *vc = vc_cons[unit].d;
328         int oldidx = con2fb_map[unit];
329         struct fb_info *info = registered_fb[newidx];
330         struct fb_info *oldinfo = NULL;
331         int found;
332
333         if (oldidx == newidx)
334                 return 0;
335
336         if (!vc)
337             return -ENODEV;
338
339         if (!search_for_mapped_con()) {
340                 info_idx = newidx;
341                 fb_console_init();
342                 return 0;
343         }
344
345         if (oldidx != -1)
346                 oldinfo = registered_fb[oldidx];
347
348         found = search_fb_in_map(newidx);
349
350         acquire_console_sem();
351         con2fb_map[unit] = newidx;
352         if (!found) {
353                 if (!try_module_get(info->fbops->owner)) {
354                         con2fb_map[unit] = oldidx;
355                         release_console_sem();
356                         return -ENODEV;
357                 }
358                 if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
359                         module_put(info->fbops->owner);
360                         con2fb_map[unit] = oldidx;
361                         release_console_sem();
362                         return -ENODEV;
363                 }
364         }
365
366         /*
367          * If old fb is not mapped to any of the consoles,
368          * fbcon should release it.
369          */
370         if (oldinfo && !search_fb_in_map(oldidx)) {
371                 if (oldinfo->fbops->fb_release &&
372                     oldinfo->fbops->fb_release(oldinfo, 0)) {
373                         con2fb_map[unit] = oldidx;
374                         if (!found && info->fbops->fb_release)
375                                 info->fbops->fb_release(info, 0);
376                         if (!found)
377                                 module_put(info->fbops->owner);
378                         release_console_sem();
379                         return -ENODEV;
380                 }
381                 if (oldinfo->queue.func == fb_flashcursor)
382                         del_timer_sync(&oldinfo->cursor_timer);
383                 module_put(oldinfo->fbops->owner);
384         }
385         info->currcon = -1;
386         if (!found) {
387                 if (!info->queue.func || info->queue.func == fb_flashcursor) {
388                         if (!info->queue.func)
389                                 INIT_WORK(&info->queue, fb_flashcursor, info);
390
391                         init_timer(&info->cursor_timer);
392                         info->cursor_timer.function = cursor_timer_handler;
393                         info->cursor_timer.expires = jiffies + HZ / 5;
394                         info->cursor_timer.data = (unsigned long ) info;
395                         add_timer(&info->cursor_timer);
396                 }
397         }
398         if (info->fbops->fb_set_par)
399                 info->fbops->fb_set_par(info);
400         fbcon_set_disp(info, vc);
401         release_console_sem();
402         return 0;
403 }
404
405 /*
406  * Accelerated handlers.
407  */
408 void accel_bmove(struct vc_data *vc, struct fb_info *info, int sy, 
409                 int sx, int dy, int dx, int height, int width)
410 {
411         struct fb_copyarea area;
412
413         area.sx = sx * vc->vc_font.width;
414         area.sy = sy * vc->vc_font.height;
415         area.dx = dx * vc->vc_font.width;
416         area.dy = dy * vc->vc_font.height;
417         area.height = height * vc->vc_font.height;
418         area.width = width * vc->vc_font.width;
419
420         info->fbops->fb_copyarea(info, &area);
421 }
422
423 void accel_clear(struct vc_data *vc, struct fb_info *info, int sy,
424                         int sx, int height, int width)
425 {
426         int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
427         struct fb_fillrect region;
428
429         region.color = attr_bgcol_ec(bgshift, vc);
430         region.dx = sx * vc->vc_font.width;
431         region.dy = sy * vc->vc_font.height;
432         region.width = width * vc->vc_font.width;
433         region.height = height * vc->vc_font.height;
434         region.rop = ROP_COPY;
435
436         info->fbops->fb_fillrect(info, &region);
437 }       
438
439 void accel_putcs(struct vc_data *vc, struct fb_info *info,
440                  const unsigned short *s, int count, int yy, int xx)
441 {
442         void (*move_unaligned)(struct fb_info *info, struct fb_pixmap *buf,
443                                u8 *dst, u32 d_pitch, u8 *src, u32 idx,
444                                u32 height, u32 shift_high, u32 shift_low,
445                                u32 mod);
446         void (*move_aligned)(struct fb_info *info, struct fb_pixmap *buf,
447                              u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch,
448                              u32 height);
449         unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
450         unsigned int width = (vc->vc_font.width + 7) >> 3;
451         unsigned int cellsize = vc->vc_font.height * width;
452         unsigned int maxcnt = info->pixmap.size/cellsize;
453         unsigned int scan_align = info->pixmap.scan_align - 1;
454         unsigned int buf_align = info->pixmap.buf_align - 1;
455         unsigned int shift_low = 0, mod = vc->vc_font.width % 8;
456         unsigned int shift_high = 8, pitch, cnt, size, k;
457         unsigned int idx = vc->vc_font.width >> 3;
458         struct fb_image image;
459         u8 *src, *dst;
460
461         image.fg_color = attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8,
462                                     scr_readw(s));
463         image.bg_color = attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12,
464                                     scr_readw(s));
465         image.dx = xx * vc->vc_font.width;
466         image.dy = yy * vc->vc_font.height;
467         image.height = vc->vc_font.height;
468         image.depth = 1;
469
470         if (info->pixmap.outbuf && info->pixmap.inbuf) {
471                 move_aligned = fb_iomove_buf_aligned;
472                 move_unaligned = fb_iomove_buf_unaligned;
473         } else {
474                 move_aligned = fb_sysmove_buf_aligned;
475                 move_unaligned = fb_sysmove_buf_unaligned;
476         }
477         while (count) {
478                 if (count > maxcnt)
479                         cnt = k = maxcnt;
480                 else
481                         cnt = k = count;
482
483                 image.width = vc->vc_font.width * cnt;
484                 pitch = ((image.width + 7) >> 3) + scan_align;
485                 pitch &= ~scan_align;
486                 size = pitch * image.height + buf_align;
487                 size &= ~buf_align;
488                 dst = fb_get_buffer_offset(info, &info->pixmap, size);
489                 image.data = dst;
490                 if (mod) {
491                         while (k--) {
492                                 src = vc->vc_font.data + (scr_readw(s++)&
493                                                           charmask)*cellsize;
494                                 move_unaligned(info, &info->pixmap, dst, pitch,
495                                                src, idx, image.height,
496                                                shift_high, shift_low, mod);
497                                 shift_low += mod;
498                                 dst += (shift_low >= 8) ? width : width - 1;
499                                 shift_low &= 7;
500                                 shift_high = 8 - shift_low;
501                         }
502                 } else {
503                         while (k--) {
504                                 src = vc->vc_font.data + (scr_readw(s++)&
505                                                           charmask)*cellsize;
506                                 move_aligned(info, &info->pixmap, dst, pitch,
507                                              src, idx, image.height);
508                                 dst += width;
509                         }
510                 }
511                 info->fbops->fb_imageblit(info, &image);
512                 image.dx += cnt * vc->vc_font.width;
513                 count -= cnt;
514         }
515 }
516
517 void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
518                                 int bottom_only)
519 {
520         int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
521         unsigned int cw = vc->vc_font.width;
522         unsigned int ch = vc->vc_font.height;
523         unsigned int rw = info->var.xres - (vc->vc_cols*cw);
524         unsigned int bh = info->var.yres - (vc->vc_rows*ch);
525         unsigned int rs = info->var.xres - rw;
526         unsigned int bs = info->var.yres - bh;
527         struct fb_fillrect region;
528
529         region.color = attr_bgcol_ec(bgshift, vc);
530         region.rop = ROP_COPY;
531
532         if (rw && !bottom_only) {
533                 region.dx = info->var.xoffset + rs;
534                 region.dy = 0;
535                 region.width = rw;
536                 region.height = info->var.yres_virtual;
537                 info->fbops->fb_fillrect(info, &region);
538         }
539
540         if (bh) {
541                 region.dx = info->var.xoffset;
542                 region.dy = info->var.yoffset + bs;
543                 region.width = rs;
544                 region.height = bh;
545                 info->fbops->fb_fillrect(info, &region);
546         }       
547 }       
548
549 /*
550  *  Low Level Operations
551  */
552 /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */
553 static const char *fbcon_startup(void)
554 {
555         const char *display_desc = "frame buffer device";
556         struct display *p = &fb_display[fg_console];
557         struct vc_data *vc = vc_cons[fg_console].d;
558         struct font_desc *font = NULL;
559         struct module *owner;
560         struct fb_info *info = NULL;
561         int rows, cols;
562         int irqres;
563
564         irqres = 1;
565         /*
566          *  If num_registered_fb is zero, this is a call for the dummy part.
567          *  The frame buffer devices weren't initialized yet.
568          */
569         if (!num_registered_fb || info_idx == -1)
570                 return display_desc;
571         /*
572          * Instead of blindly using registered_fb[0], we use info_idx, set by
573          * fb_console_init();
574          */
575         info = registered_fb[info_idx];
576         if (!info)
577                 return NULL;
578         info->currcon = -1;
579         
580         owner = info->fbops->owner;
581         if (!try_module_get(owner))
582                 return NULL;
583         if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
584                 module_put(owner);
585                 return NULL;
586         }
587         if (info->fix.type != FB_TYPE_TEXT) {
588                 if (fbcon_softback_size) {
589                         if (!softback_buf) {
590                                 softback_buf =
591                                     (unsigned long)
592                                     kmalloc(fbcon_softback_size,
593                                             GFP_KERNEL);
594                                 if (!softback_buf) {
595                                         fbcon_softback_size = 0;
596                                         softback_top = 0;
597                                 }
598                         }
599                 } else {
600                         if (softback_buf) {
601                                 kfree((void *) softback_buf);
602                                 softback_buf = 0;
603                                 softback_top = 0;
604                         }
605                 }
606                 if (softback_buf)
607                         softback_in = softback_top = softback_curr =
608                             softback_buf;
609                 softback_lines = 0;
610         }
611
612         /* Setup default font */
613         if (!p->fontdata) {
614                 if (!fontname[0] || !(font = find_font(fontname)))
615                         font = get_default_font(info->var.xres,
616                                                    info->var.yres);
617                 vc->vc_font.width = font->width;
618                 vc->vc_font.height = font->height;
619                 vc->vc_font.data = p->fontdata = font->data;
620                 vc->vc_font.charcount = 256; /* FIXME  Need to support more fonts */
621         }
622
623         cols = info->var.xres / vc->vc_font.width;
624         rows = info->var.yres / vc->vc_font.height;
625         vc_resize(vc->vc_num, cols, rows);
626
627         DPRINTK("mode:   %s\n", info->fix.id);
628         DPRINTK("visual: %d\n", info->fix.visual);
629         DPRINTK("res:    %dx%d-%d\n", info->var.xres,
630                 info->var.yres,
631                 info->var.bits_per_pixel);
632         con_set_default_unimap(vc->vc_num);
633
634 #ifdef CONFIG_ATARI
635         if (MACH_IS_ATARI) {
636                 cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
637                 irqres =
638                     request_irq(IRQ_AUTO_4, fb_vbl_handler,
639                                 IRQ_TYPE_PRIO, "framebuffer vbl",
640                                 info);
641         }
642 #endif                          /* CONFIG_ATARI */
643
644 #ifdef CONFIG_MAC
645         /*
646          * On a Macintoy, the VBL interrupt may or may not be active. 
647          * As interrupt based cursor is more reliable and race free, we 
648          * probe for VBL interrupts.
649          */
650         if (MACH_IS_MAC) {
651                 int ct = 0;
652                 /*
653                  * Probe for VBL: set temp. handler ...
654                  */
655                 irqres = request_irq(IRQ_MAC_VBL, fb_vbl_detect, 0,
656                                      "framebuffer vbl", info);
657                 vbl_detected = 0;
658
659                 /*
660                  * ... and spin for 20 ms ...
661                  */
662                 while (!vbl_detected && ++ct < 1000)
663                         udelay(20);
664
665                 if (ct == 1000)
666                         printk
667                             ("fbcon_startup: No VBL detected, using timer based cursor.\n");
668
669                 free_irq(IRQ_MAC_VBL, fb_vbl_detect);
670
671                 if (vbl_detected) {
672                         /*
673                          * interrupt based cursor ok
674                          */
675                         cursor_blink_rate = MAC_CURSOR_BLINK_RATE;
676                         irqres =
677                             request_irq(IRQ_MAC_VBL, fb_vbl_handler, 0,
678                                         "framebuffer vbl", info);
679                 } else {
680                         /*
681                          * VBL not detected: fall through, use timer based cursor
682                          */
683                         irqres = 1;
684                 }
685         }
686 #endif                          /* CONFIG_MAC */
687
688 #if defined(__arm__) && defined(IRQ_VSYNCPULSE)
689         cursor_blink_rate = ARM_CURSOR_BLINK_RATE;
690         irqres = request_irq(IRQ_VSYNCPULSE, fb_vbl_handler, SA_SHIRQ,
691                              "framebuffer vbl", info);
692 #endif
693         /* Initialize the work queue. If the driver provides its
694          * own work queue this means it will use something besides 
695          * default timer to flash the cursor. */
696         if (!info->queue.func) {
697                 INIT_WORK(&info->queue, fb_flashcursor, info);
698
699                 init_timer(&info->cursor_timer);
700                 info->cursor_timer.function = cursor_timer_handler;
701                 info->cursor_timer.expires = jiffies + HZ / 5;
702                 info->cursor_timer.data = (unsigned long ) info;
703                 add_timer(&info->cursor_timer);
704         }
705         return display_desc;
706 }
707
708 static void fbcon_init(struct vc_data *vc, int init)
709 {
710         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
711         struct vc_data **default_mode = vc->vc_display_fg;
712         struct display *t, *p = &fb_display[vc->vc_num];
713         int display_fg = (*default_mode)->vc_num;
714         int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;
715         unsigned short *save = NULL, *r, *q;
716         int cap = info->flags;
717
718         if (info_idx == -1 || info == NULL)
719             return;
720         if (vc->vc_num != display_fg || (info->flags & FBINFO_MODULE) ||
721             (info->fix.type == FB_TYPE_TEXT))
722                 logo = 0;
723
724         info->var.xoffset = info->var.yoffset = p->yscroll = 0; /* reset wrap/pan */
725
726         /* If we are not the first console on this
727            fb, copy the font from that console */
728         t = &fb_display[display_fg];
729         if (!vc->vc_font.data) {
730                 vc->vc_font.data = p->fontdata = t->fontdata;
731                 vc->vc_font.width = (*default_mode)->vc_font.width;
732                 vc->vc_font.height = (*default_mode)->vc_font.height;
733                 p->userfont = t->userfont;
734                 if (p->userfont)
735                         REFCOUNT(p->fontdata)++;
736                 con_copy_unimap(vc->vc_num, display_fg);
737         }
738         if (p->userfont)
739                 charcnt = FNTCHARCNT(p->fontdata);
740         vc->vc_can_do_color = info->var.bits_per_pixel != 1;
741         vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
742         if (charcnt == 256) {
743                 vc->vc_hi_font_mask = 0;
744         } else {
745                 vc->vc_hi_font_mask = 0x100;
746                 if (vc->vc_can_do_color)
747                         vc->vc_complement_mask <<= 1;
748         }
749         cols = vc->vc_cols;
750         rows = vc->vc_rows;
751         new_cols = info->var.xres / vc->vc_font.width;
752         new_rows = info->var.yres / vc->vc_font.height;
753         vc_resize(vc->vc_num, new_cols, new_rows);
754         /*
755          * We must always set the mode. The mode of the previous console
756          * driver could be in the same resolution but we are using different
757          * hardware so we have to initialize the hardware.
758          *
759          * We need to do it in fbcon_init() to prevent screen corruption.
760          */
761         if (CON_IS_VISIBLE(vc) && info->fbops->fb_set_par)
762                 info->fbops->fb_set_par(info);
763
764
765         if ((cap & FBINFO_HWACCEL_COPYAREA) &&
766             !(cap & FBINFO_HWACCEL_DISABLED))
767                 p->scrollmode = SCROLL_MOVE;
768         else /* default to something safe */
769                 p->scrollmode = SCROLL_REDRAW;
770
771         /*
772          *  ++guenther: console.c:vc_allocate() relies on initializing
773          *  vc_{cols,rows}, but we must not set those if we are only
774          *  resizing the console.
775          */
776         if (!init) {
777                 vc->vc_cols = new_cols;
778                 vc->vc_rows = new_rows;
779         }
780
781         if (logo) {
782                 /* Need to make room for the logo */
783                 int cnt;
784                 int step;
785
786                 logo_height = fb_prepare_logo(info);
787                 logo_lines = (logo_height + vc->vc_font.height - 1) /
788                              vc->vc_font.height;
789                 q = (unsigned short *) (vc->vc_origin +
790                                         vc->vc_size_row * rows);
791                 step = logo_lines * cols;
792                 for (r = q - logo_lines * cols; r < q; r++)
793                         if (scr_readw(r) != vc->vc_video_erase_char)
794                                 break;
795                 if (r != q && new_rows >= rows + logo_lines) {
796                         save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL);
797                         if (save) {
798                                 int i = cols < new_cols ? cols : new_cols;
799                                 scr_memsetw(save, vc->vc_video_erase_char,
800                                             logo_lines * new_cols * 2);
801                                 r = q - step;
802                                 for (cnt = 0; cnt < logo_lines; cnt++, r += i)
803                                         scr_memcpyw(save + cnt * new_cols, r, 2 * i);
804                                 r = q;
805                         }
806                 }
807                 if (r == q) {
808                         /* We can scroll screen down */
809                         r = q - step - cols;
810                         for (cnt = rows - logo_lines; cnt > 0; cnt--) {
811                                 scr_memcpyw(r + step, r, vc->vc_size_row);
812                                 r -= cols;
813                         }
814                         if (!save) {
815                                 vc->vc_y += logo_lines;
816                                 vc->vc_pos += logo_lines * vc->vc_size_row;
817                         }
818                 }
819                 scr_memsetw((unsigned short *) vc->vc_origin,
820                             vc->vc_video_erase_char,
821                             vc->vc_size_row * logo_lines);
822
823                 if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
824                         accel_clear_margins(vc, info, 0);
825                         update_screen(vc->vc_num);
826                 }
827                 if (save) {
828                         q = (unsigned short *) (vc->vc_origin +
829                                                 vc->vc_size_row *
830                                                 rows);
831                         scr_memcpyw(q, save, logo_lines * new_cols * 2);
832                         vc->vc_y += logo_lines;
833                         vc->vc_pos += logo_lines * vc->vc_size_row;
834                         kfree(save);
835                 }
836                 if (logo_lines > vc->vc_bottom) {
837                         logo_shown = -1;
838                         printk(KERN_INFO
839                                "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
840                 } else {
841                         logo_shown = -2;
842                         vc->vc_top = logo_lines;
843                 }
844         }
845
846         if (vc->vc_num == display_fg && softback_buf) {
847                 int l = fbcon_softback_size / vc->vc_size_row;
848                 if (l > 5)
849                         softback_end = softback_buf + l * vc->vc_size_row;
850                 else {
851                         /* Smaller scrollback makes no sense, and 0 would screw
852                            the operation totally */
853                         softback_top = 0;
854                 }
855         }
856 }
857
858 static void fbcon_deinit(struct vc_data *vc)
859 {
860         struct display *p = &fb_display[vc->vc_num];
861
862         if (info_idx != -1)
863             return;
864         fbcon_free_font(p);
865 }
866
867 /* ====================================================================== */
868
869 /*  fbcon_XXX routines - interface used by the world
870  *
871  *  This system is now divided into two levels because of complications
872  *  caused by hardware scrolling. Top level functions:
873  *
874  *      fbcon_bmove(), fbcon_clear(), fbcon_putc()
875  *
876  *  handles y values in range [0, scr_height-1] that correspond to real
877  *  screen positions. y_wrap shift means that first line of bitmap may be
878  *  anywhere on this display. These functions convert lineoffsets to
879  *  bitmap offsets and deal with the wrap-around case by splitting blits.
880  *
881  *      fbcon_bmove_physical_8()    -- These functions fast implementations
882  *      fbcon_clear_physical_8()    -- of original fbcon_XXX fns.
883  *      fbcon_putc_physical_8()     -- (font width != 8) may be added later
884  *
885  *  WARNING:
886  *
887  *  At the moment fbcon_putc() cannot blit across vertical wrap boundary
888  *  Implies should only really hardware scroll in rows. Only reason for
889  *  restriction is simplicity & efficiency at the moment.
890  */
891
892 static __inline__ int real_y(struct display *p, int ypos)
893 {
894         int rows = p->vrows;
895
896         ypos += p->yscroll;
897         return ypos < rows ? ypos : ypos - rows;
898 }
899
900
901 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
902                         int width)
903 {
904         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
905         
906         struct display *p = &fb_display[vc->vc_num];
907         u_int y_break;
908
909         if (!info->fbops->fb_blank && console_blanked)
910                 return;
911         if (info->state != FBINFO_STATE_RUNNING)
912                 return;
913
914         if (!height || !width)
915                 return;
916
917         /* Split blits that cross physical y_wrap boundary */
918
919         y_break = p->vrows - p->yscroll;
920         if (sy < y_break && sy + height - 1 >= y_break) {
921                 u_int b = y_break - sy;
922                 accel_clear(vc, info, real_y(p, sy), sx, b, width);
923                 accel_clear(vc, info, real_y(p, sy + b), sx, height - b,
924                                  width);
925         } else
926                 accel_clear(vc, info, real_y(p, sy), sx, height, width);
927 }
928
929 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
930 {
931         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
932         unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
933         unsigned int scan_align = info->pixmap.scan_align - 1;
934         unsigned int buf_align = info->pixmap.buf_align - 1;
935         unsigned int width = (vc->vc_font.width + 7) >> 3;
936         int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
937         int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
938         struct display *p = &fb_display[vc->vc_num];
939         unsigned int size, pitch;
940         struct fb_image image;
941         u8 *src, *dst;
942
943         if (!info->fbops->fb_blank && console_blanked)
944                 return;
945         if (info->state != FBINFO_STATE_RUNNING)
946                 return;
947
948         if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
949                 return;
950
951         image.dx = xpos * vc->vc_font.width;
952         image.dy = real_y(p, ypos) * vc->vc_font.height;
953         image.width = vc->vc_font.width;
954         image.height = vc->vc_font.height;
955         image.fg_color = attr_fgcol(fgshift, c);
956         image.bg_color = attr_bgcol(bgshift, c);
957         image.depth = 1;
958
959         src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * width;
960
961         pitch = width + scan_align;
962         pitch &= ~scan_align;
963         size = pitch * vc->vc_font.height;
964         size += buf_align;
965         size &= ~buf_align;
966
967         dst = fb_get_buffer_offset(info, &info->pixmap, size);
968         image.data = dst;
969
970         if (info->pixmap.outbuf)
971                 fb_iomove_buf_aligned(info, &info->pixmap, dst, pitch, src, width, image.height);
972         else
973                 fb_sysmove_buf_aligned(info, &info->pixmap, dst, pitch, src, width, image.height);
974
975         info->fbops->fb_imageblit(info, &image);
976 }
977
978 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
979                         int count, int ypos, int xpos)
980 {
981         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
982         struct display *p = &fb_display[vc->vc_num];
983
984         if (!info->fbops->fb_blank && console_blanked)
985                 return;
986         if (info->state != FBINFO_STATE_RUNNING)
987                 return;
988
989         if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
990                 return;
991
992         accel_putcs(vc, info, s, count, real_y(p, ypos), xpos);
993 }
994
995 static void fbcon_cursor(struct vc_data *vc, int mode)
996 {
997         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
998         unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
999         int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
1000         int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
1001         struct display *p = &fb_display[vc->vc_num];
1002         int w = (vc->vc_font.width + 7) >> 3, c;
1003         int y = real_y(p, vc->vc_y);
1004         struct fb_cursor cursor;
1005         
1006         if (mode & CM_SOFTBACK) {
1007                 mode &= ~CM_SOFTBACK;
1008                 if (softback_lines) {
1009                         if (y + softback_lines >= vc->vc_rows)
1010                                 mode = CM_ERASE;
1011                         else
1012                                 y += softback_lines;
1013                 }
1014         } else if (softback_lines)
1015                 fbcon_set_origin(vc);
1016
1017         c = scr_readw((u16 *) vc->vc_pos);
1018
1019         cursor.image.data = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
1020         cursor.set = FB_CUR_SETCUR;
1021         cursor.image.depth = 1;
1022         
1023         switch (mode) {
1024         case CM_ERASE:
1025                 if (info->cursor.rop == ROP_XOR) {
1026                         info->cursor.enable = 0;
1027                         info->cursor.rop = ROP_COPY;
1028                         info->fbops->fb_cursor(info, &cursor);
1029                 }       
1030                 break;
1031         case CM_MOVE:
1032         case CM_DRAW:
1033                 info->cursor.enable = 1;
1034                 
1035                 if (info->cursor.image.fg_color != attr_fgcol(fgshift, c) ||
1036                     info->cursor.image.bg_color != attr_bgcol(bgshift, c)) {
1037                         cursor.image.fg_color = attr_fgcol(fgshift, c);
1038                         cursor.image.bg_color = attr_bgcol(bgshift, c);
1039                         cursor.set |= FB_CUR_SETCMAP;
1040                 }
1041                 
1042                 if ((info->cursor.image.dx != (vc->vc_font.width * vc->vc_x)) ||
1043                     (info->cursor.image.dy != (vc->vc_font.height * y))) {
1044                         cursor.image.dx = vc->vc_font.width * vc->vc_x;
1045                         cursor.image.dy = vc->vc_font.height * y;
1046                         cursor.set |= FB_CUR_SETPOS;
1047                 }
1048
1049                 if (info->cursor.image.height != vc->vc_font.height ||
1050                     info->cursor.image.width != vc->vc_font.width) {
1051                         cursor.image.height = vc->vc_font.height;
1052                         cursor.image.width = vc->vc_font.width;
1053                         cursor.set |= FB_CUR_SETSIZE;
1054                 }
1055
1056                 if (info->cursor.hot.x || info->cursor.hot.y) {
1057                         cursor.hot.x = cursor.hot.y = 0;
1058                         cursor.set |= FB_CUR_SETHOT;
1059                 }
1060
1061                 if ((cursor.set & FB_CUR_SETSIZE) || ((vc->vc_cursor_type & 0x0f) != p->cursor_shape)
1062                     || info->cursor.mask == NULL) {
1063                         char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
1064                         int cur_height, size, i = 0;
1065
1066                         if (!mask)      return; 
1067                 
1068                         if (info->cursor.mask)
1069                                 kfree(info->cursor.mask);
1070                         info->cursor.mask = mask;
1071         
1072                         p->cursor_shape = vc->vc_cursor_type & 0x0f;
1073                         cursor.set |= FB_CUR_SETSHAPE;
1074
1075                         switch (vc->vc_cursor_type & 0x0f) {
1076                         case CUR_NONE:
1077                                 cur_height = 0;
1078                                 break;
1079                         case CUR_UNDERLINE:
1080                                 cur_height = (vc->vc_font.height < 10) ? 1 : 2;
1081                                 break;
1082                         case CUR_LOWER_THIRD:
1083                                 cur_height = vc->vc_font.height/3;
1084                                 break;
1085                         case CUR_LOWER_HALF:
1086                                 cur_height = vc->vc_font.height >> 1;
1087                                 break;
1088                         case CUR_TWO_THIRDS:
1089                                 cur_height = (vc->vc_font.height << 1)/3;
1090                                 break;
1091                         case CUR_BLOCK:
1092                         default:
1093                                 cur_height = vc->vc_font.height;
1094                                 break;
1095                         }
1096                         size = (vc->vc_font.height - cur_height) * w;
1097                         while (size--)
1098                                 mask[i++] = 0;
1099                         size = cur_height * w;
1100                         while (size--)
1101                                 mask[i++] = 0xff;
1102                 }
1103                 info->cursor.rop = ROP_XOR;
1104                 info->fbops->fb_cursor(info, &cursor);
1105                 vbl_cursor_cnt = CURSOR_DRAW_DELAY;
1106                 break;
1107         }
1108 }
1109
1110 static int scrollback_phys_max = 0;
1111 static int scrollback_max = 0;
1112 static int scrollback_current = 0;
1113
1114 int update_var(int con, struct fb_info *info)
1115 {
1116         if (con == info->currcon) 
1117                 return fb_pan_display(info, &info->var);
1118         return 0;
1119 }
1120
1121 static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc)
1122 {
1123         struct display *p = &fb_display[vc->vc_num], *t;
1124         struct vc_data **default_mode = vc->vc_display_fg;
1125         int display_fg = (*default_mode)->vc_num;
1126         int rows, cols, charcnt = 256;
1127
1128         info->var.xoffset = info->var.yoffset = p->yscroll = 0;
1129         t = &fb_display[display_fg];
1130         if (!vc->vc_font.data) {
1131                 vc->vc_font.data = p->fontdata = t->fontdata;
1132                 vc->vc_font.width = (*default_mode)->vc_font.width;
1133                 vc->vc_font.height = (*default_mode)->vc_font.height;
1134                 p->userfont = t->userfont;
1135                 if (p->userfont)
1136                         REFCOUNT(p->fontdata)++;
1137                 con_copy_unimap(vc->vc_num, display_fg);
1138         }
1139         if (p->userfont)
1140                 charcnt = FNTCHARCNT(p->fontdata);
1141
1142         vc->vc_can_do_color = info->var.bits_per_pixel != 1;
1143         vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1144         if (charcnt == 256) {
1145                 vc->vc_hi_font_mask = 0;
1146         } else {
1147                 vc->vc_hi_font_mask = 0x100;
1148                 if (vc->vc_can_do_color)
1149                         vc->vc_complement_mask <<= 1;
1150         }
1151         cols = info->var.xres / vc->vc_font.width;
1152         rows = info->var.yres / vc->vc_font.height;
1153         vc_resize(vc->vc_num, cols, rows);
1154         if (CON_IS_VISIBLE(vc)) {
1155                 update_screen(vc->vc_num);
1156                 if (softback_buf) {
1157                         int l = fbcon_softback_size / vc->vc_size_row;
1158
1159                         if (l > 5)
1160                                 softback_end = softback_buf + l *
1161                                         vc->vc_size_row;
1162                         else {
1163                                 /* Smaller scrollback makes no sense, and 0
1164                                    would screw the operation totally */
1165                                 softback_top = 0;
1166                         }
1167                 }
1168         }
1169         switch_screen(fg_console);
1170 }
1171
1172 static __inline__ void ywrap_up(struct vc_data *vc, int count)
1173 {
1174         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1175         struct display *p = &fb_display[vc->vc_num];
1176         
1177         p->yscroll += count;
1178         if (p->yscroll >= p->vrows)     /* Deal with wrap */
1179                 p->yscroll -= p->vrows;
1180         info->var.xoffset = 0;
1181         info->var.yoffset = p->yscroll * vc->vc_font.height;
1182         info->var.vmode |= FB_VMODE_YWRAP;
1183         update_var(vc->vc_num, info);
1184         scrollback_max += count;
1185         if (scrollback_max > scrollback_phys_max)
1186                 scrollback_max = scrollback_phys_max;
1187         scrollback_current = 0;
1188 }
1189
1190 static __inline__ void ywrap_down(struct vc_data *vc, int count)
1191 {
1192         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1193         struct display *p = &fb_display[vc->vc_num];
1194         
1195         p->yscroll -= count;
1196         if (p->yscroll < 0)     /* Deal with wrap */
1197                 p->yscroll += p->vrows;
1198         info->var.xoffset = 0;
1199         info->var.yoffset = p->yscroll * vc->vc_font.height;
1200         info->var.vmode |= FB_VMODE_YWRAP;
1201         update_var(vc->vc_num, info);
1202         scrollback_max -= count;
1203         if (scrollback_max < 0)
1204                 scrollback_max = 0;
1205         scrollback_current = 0;
1206 }
1207
1208 static __inline__ void ypan_up(struct vc_data *vc, int count)
1209 {
1210         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1211         struct display *p = &fb_display[vc->vc_num];
1212         
1213         p->yscroll += count;
1214         if (p->yscroll > p->vrows - vc->vc_rows) {
1215                 accel_bmove(vc, info, p->vrows - vc->vc_rows, 
1216                                 0, 0, 0, vc->vc_rows, vc->vc_cols);
1217                 p->yscroll -= p->vrows - vc->vc_rows;
1218         }
1219         info->var.xoffset = 0;
1220         info->var.yoffset = p->yscroll * vc->vc_font.height;
1221         info->var.vmode &= ~FB_VMODE_YWRAP;
1222         update_var(vc->vc_num, info);
1223         accel_clear_margins(vc, info, 1);
1224         scrollback_max += count;
1225         if (scrollback_max > scrollback_phys_max)
1226                 scrollback_max = scrollback_phys_max;
1227         scrollback_current = 0;
1228 }
1229
1230 static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
1231 {
1232         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1233         struct display *p = &fb_display[vc->vc_num];
1234         int redraw = 0;
1235
1236         p->yscroll += count;
1237         if (p->yscroll > p->vrows - vc->vc_rows) {
1238                 p->yscroll -= p->vrows - vc->vc_rows;
1239                 redraw = 1;
1240         }
1241
1242         info->var.xoffset = 0;
1243         info->var.yoffset = p->yscroll * vc->vc_font.height;
1244         info->var.vmode &= ~FB_VMODE_YWRAP;
1245         if (redraw)
1246                 fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
1247         update_var(vc->vc_num, info);
1248         accel_clear_margins(vc, info, 1);
1249         scrollback_max += count;
1250         if (scrollback_max > scrollback_phys_max)
1251                 scrollback_max = scrollback_phys_max;
1252         scrollback_current = 0;
1253 }
1254
1255 static __inline__ void ypan_down(struct vc_data *vc, int count)
1256 {
1257         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1258         struct display *p = &fb_display[vc->vc_num];
1259         
1260         p->yscroll -= count;
1261         if (p->yscroll < 0) {
1262                 accel_bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
1263                                 0, vc->vc_rows, vc->vc_cols);
1264                 p->yscroll += p->vrows - vc->vc_rows;
1265         }
1266         info->var.xoffset = 0;
1267         info->var.yoffset = p->yscroll * vc->vc_font.height;
1268         info->var.vmode &= ~FB_VMODE_YWRAP;
1269         update_var(vc->vc_num, info);
1270         accel_clear_margins(vc, info, 1);
1271         scrollback_max -= count;
1272         if (scrollback_max < 0)
1273                 scrollback_max = 0;
1274         scrollback_current = 0;
1275 }
1276
1277 static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
1278 {
1279         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1280         struct display *p = &fb_display[vc->vc_num];
1281         int redraw = 0;
1282
1283         p->yscroll -= count;
1284         if (p->yscroll < 0) {
1285                 p->yscroll += p->vrows - vc->vc_rows;
1286                 redraw = 1;
1287         }
1288         info->var.xoffset = 0;
1289         info->var.yoffset = p->yscroll * vc->vc_font.height;
1290         info->var.vmode &= ~FB_VMODE_YWRAP;
1291         if (redraw)
1292                 fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
1293         update_var(vc->vc_num, info);
1294         accel_clear_margins(vc, info, 1);
1295         scrollback_max -= count;
1296         if (scrollback_max < 0)
1297                 scrollback_max = 0;
1298         scrollback_current = 0;
1299 }
1300
1301 static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
1302                                   long delta)
1303 {
1304         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1305         int count = vc->vc_rows;
1306         unsigned short *d, *s;
1307         unsigned long n;
1308         int line = 0;
1309
1310         d = (u16 *) softback_curr;
1311         if (d == (u16 *) softback_in)
1312                 d = (u16 *) vc->vc_origin;
1313         n = softback_curr + delta * vc->vc_size_row;
1314         softback_lines -= delta;
1315         if (delta < 0) {
1316                 if (softback_curr < softback_top && n < softback_buf) {
1317                         n += softback_end - softback_buf;
1318                         if (n < softback_top) {
1319                                 softback_lines -=
1320                                     (softback_top - n) / vc->vc_size_row;
1321                                 n = softback_top;
1322                         }
1323                 } else if (softback_curr >= softback_top
1324                            && n < softback_top) {
1325                         softback_lines -=
1326                             (softback_top - n) / vc->vc_size_row;
1327                         n = softback_top;
1328                 }
1329         } else {
1330                 if (softback_curr > softback_in && n >= softback_end) {
1331                         n += softback_buf - softback_end;
1332                         if (n > softback_in) {
1333                                 n = softback_in;
1334                                 softback_lines = 0;
1335                         }
1336                 } else if (softback_curr <= softback_in && n > softback_in) {
1337                         n = softback_in;
1338                         softback_lines = 0;
1339                 }
1340         }
1341         if (n == softback_curr)
1342                 return;
1343         softback_curr = n;
1344         s = (u16 *) softback_curr;
1345         if (s == (u16 *) softback_in)
1346                 s = (u16 *) vc->vc_origin;
1347         while (count--) {
1348                 unsigned short *start;
1349                 unsigned short *le;
1350                 unsigned short c;
1351                 int x = 0;
1352                 unsigned short attr = 1;
1353
1354                 start = s;
1355                 le = advance_row(s, 1);
1356                 do {
1357                         c = scr_readw(s);
1358                         if (attr != (c & 0xff00)) {
1359                                 attr = c & 0xff00;
1360                                 if (s > start) {
1361                                         accel_putcs(vc, info, start, s - start,
1362                                                     real_y(p, line), x);
1363                                         x += s - start;
1364                                         start = s;
1365                                 }
1366                         }
1367                         if (c == scr_readw(d)) {
1368                                 if (s > start) {
1369                                         accel_putcs(vc, info, start, s - start,
1370                                                     real_y(p, line), x);
1371                                         x += s - start + 1;
1372                                         start = s + 1;
1373                                 } else {
1374                                         x++;
1375                                         start++;
1376                                 }
1377                         }
1378                         s++;
1379                         d++;
1380                 } while (s < le);
1381                 if (s > start)
1382                         accel_putcs(vc, info, start, s - start,
1383                                     real_y(p, line), x);
1384                 line++;
1385                 if (d == (u16 *) softback_end)
1386                         d = (u16 *) softback_buf;
1387                 if (d == (u16 *) softback_in)
1388                         d = (u16 *) vc->vc_origin;
1389                 if (s == (u16 *) softback_end)
1390                         s = (u16 *) softback_buf;
1391                 if (s == (u16 *) softback_in)
1392                         s = (u16 *) vc->vc_origin;
1393         }
1394 }
1395
1396 static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
1397                               int line, int count, int dy)
1398 {
1399         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1400         unsigned short *s = (unsigned short *)
1401                 (vc->vc_origin + vc->vc_size_row * line);
1402
1403         while (count--) {
1404                 unsigned short *start = s;
1405                 unsigned short *le = advance_row(s, 1);
1406                 unsigned short c;
1407                 int x = 0;
1408                 unsigned short attr = 1;
1409
1410                 do {
1411                         c = scr_readw(s);
1412                         if (attr != (c & 0xff00)) {
1413                                 attr = c & 0xff00;
1414                                 if (s > start) {
1415                                         accel_putcs(vc, info, start, s - start,
1416                                                     real_y(p, dy), x);
1417                                         x += s - start;
1418                                         start = s;
1419                                 }
1420                         }
1421                         console_conditional_schedule();
1422                         s++;
1423                 } while (s < le);
1424                 if (s > start)
1425                         accel_putcs(vc, info, start, s - start,
1426                                     real_y(p, dy), x);
1427                 console_conditional_schedule();
1428                 dy++;
1429         }
1430 }
1431
1432 static void fbcon_redraw(struct vc_data *vc, struct display *p,
1433                          int line, int count, int offset)
1434 {
1435         unsigned short *d = (unsigned short *)
1436             (vc->vc_origin + vc->vc_size_row * line);
1437         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1438         unsigned short *s = d + offset;
1439
1440         while (count--) {
1441                 unsigned short *start = s;
1442                 unsigned short *le = advance_row(s, 1);
1443                 unsigned short c;
1444                 int x = 0;
1445                 unsigned short attr = 1;
1446
1447                 do {
1448                         c = scr_readw(s);
1449                         if (attr != (c & 0xff00)) {
1450                                 attr = c & 0xff00;
1451                                 if (s > start) {
1452                                         accel_putcs(vc, info, start, s - start,
1453                                                     real_y(p, line), x);
1454                                         x += s - start;
1455                                         start = s;
1456                                 }
1457                         }
1458                         if (c == scr_readw(d)) {
1459                                 if (s > start) {
1460                                         accel_putcs(vc, info, start, s - start,
1461                                                     real_y(p, line), x);
1462                                         x += s - start + 1;
1463                                         start = s + 1;
1464                                 } else {
1465                                         x++;
1466                                         start++;
1467                                 }
1468                         }
1469                         scr_writew(c, d);
1470                         console_conditional_schedule();
1471                         s++;
1472                         d++;
1473                 } while (s < le);
1474                 if (s > start)
1475                         accel_putcs(vc, info, start, s - start,
1476                                     real_y(p, line), x);
1477                 console_conditional_schedule();
1478                 if (offset > 0)
1479                         line++;
1480                 else {
1481                         line--;
1482                         /* NOTE: We subtract two lines from these pointers */
1483                         s -= vc->vc_size_row;
1484                         d -= vc->vc_size_row;
1485                 }
1486         }
1487 }
1488
1489 static inline void fbcon_softback_note(struct vc_data *vc, int t,
1490                                        int count)
1491 {
1492         unsigned short *p;
1493
1494         if (vc->vc_num != fg_console)
1495                 return;
1496         p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
1497
1498         while (count) {
1499                 scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row);
1500                 count--;
1501                 p = advance_row(p, 1);
1502                 softback_in += vc->vc_size_row;
1503                 if (softback_in == softback_end)
1504                         softback_in = softback_buf;
1505                 if (softback_in == softback_top) {
1506                         softback_top += vc->vc_size_row;
1507                         if (softback_top == softback_end)
1508                                 softback_top = softback_buf;
1509                 }
1510         }
1511         softback_curr = softback_in;
1512 }
1513
1514 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
1515                         int count)
1516 {
1517         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1518         struct display *p = &fb_display[vc->vc_num];
1519         int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
1520
1521         if (!info->fbops->fb_blank && console_blanked)
1522                 return 0;
1523
1524         if (!count || vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
1525                 return 0;
1526
1527         fbcon_cursor(vc, CM_ERASE);
1528
1529         /*
1530          * ++Geert: Only use ywrap/ypan if the console is in text mode
1531          * ++Andrew: Only use ypan on hardware text mode when scrolling the
1532          *           whole screen (prevents flicker).
1533          */
1534
1535         switch (dir) {
1536         case SM_UP:
1537                 if (count > vc->vc_rows)        /* Maximum realistic size */
1538                         count = vc->vc_rows;
1539                 if (softback_top)
1540                         fbcon_softback_note(vc, t, count);
1541                 if (logo_shown >= 0)
1542                         goto redraw_up;
1543                 switch (p->scrollmode) {
1544                 case SCROLL_MOVE:
1545                         accel_bmove(vc, info, t + count, 0, t, 0,
1546                                          b - t - count, vc->vc_cols);
1547                         accel_clear(vc, info, b - count, 0, count,
1548                                          vc->vc_cols);
1549                         break;
1550
1551                 case SCROLL_WRAP_MOVE:
1552                         if (b - t - count > 3 * vc->vc_rows >> 2) {
1553                                 if (t > 0)
1554                                         fbcon_bmove(vc, 0, 0, count, 0, t,
1555                                                     vc->vc_cols);
1556                                 ywrap_up(vc, count);
1557                                 if (vc->vc_rows - b > 0)
1558                                         fbcon_bmove(vc, b - count, 0, b, 0,
1559                                                     vc->vc_rows - b,
1560                                                     vc->vc_cols);
1561                         } else if (info->flags & FBINFO_READS_FAST)
1562                                 fbcon_bmove(vc, t + count, 0, t, 0,
1563                                             b - t - count, vc->vc_cols);
1564                         else
1565                                 goto redraw_up;
1566                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1567                         break;
1568
1569                 case SCROLL_PAN_REDRAW:
1570                         if ((p->yscroll + count <=
1571                              2 * (p->vrows - vc->vc_rows))
1572                             && ((!scroll_partial && (b - t == vc->vc_rows))
1573                                 || (scroll_partial
1574                                     && (b - t - count >
1575                                         3 * vc->vc_rows >> 2)))) {
1576                                 if (t > 0)
1577                                         fbcon_redraw_move(vc, p, 0, t, count);
1578                                 ypan_up_redraw(vc, t, count);
1579                                 if (vc->vc_rows - b > 0)
1580                                         fbcon_redraw_move(vc, p, b - count,
1581                                                           vc->vc_rows - b, b);
1582                         } else
1583                                 fbcon_redraw_move(vc, p, t + count, b - t - count, t);
1584                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1585                         break;
1586
1587                 case SCROLL_PAN_MOVE:
1588                         if ((p->yscroll + count <=
1589                              2 * (p->vrows - vc->vc_rows))
1590                             && ((!scroll_partial && (b - t == vc->vc_rows))
1591                                 || (scroll_partial
1592                                     && (b - t - count >
1593                                         3 * vc->vc_rows >> 2)))) {
1594                                 if (t > 0)
1595                                         fbcon_bmove(vc, 0, 0, count, 0, t,
1596                                                     vc->vc_cols);
1597                                 ypan_up(vc, count);
1598                                 if (vc->vc_rows - b > 0)
1599                                         fbcon_bmove(vc, b - count, 0, b, 0,
1600                                                     vc->vc_rows - b,
1601                                                     vc->vc_cols);
1602                         } else if (info->flags & FBINFO_READS_FAST)
1603                                 fbcon_bmove(vc, t + count, 0, t, 0,
1604                                             b - t - count, vc->vc_cols);
1605                         else
1606                                 goto redraw_up;
1607                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1608                         break;
1609
1610                 case SCROLL_REDRAW:
1611                       redraw_up:
1612                         fbcon_redraw(vc, p, t, b - t - count,
1613                                      count * vc->vc_cols);
1614                         accel_clear(vc, info, real_y(p, b - count), 0,
1615                                          count, vc->vc_cols);
1616                         scr_memsetw((unsigned short *) (vc->vc_origin +
1617                                                         vc->vc_size_row *
1618                                                         (b - count)),
1619                                     vc->vc_video_erase_char,
1620                                     vc->vc_size_row * count);
1621                         return 1;
1622                 }
1623                 break;
1624
1625         case SM_DOWN:
1626                 if (count > vc->vc_rows)        /* Maximum realistic size */
1627                         count = vc->vc_rows;
1628                 switch (p->scrollmode) {
1629                 case SCROLL_MOVE:
1630                         accel_bmove(vc, info, t, 0, t + count, 0,
1631                                          b - t - count, vc->vc_cols);
1632                         accel_clear(vc, info, t, 0, count, vc->vc_cols);
1633                         break;
1634
1635                 case SCROLL_WRAP_MOVE:
1636                         if (b - t - count > 3 * vc->vc_rows >> 2) {
1637                                 if (vc->vc_rows - b > 0)
1638                                         fbcon_bmove(vc, b, 0, b - count, 0,
1639                                                     vc->vc_rows - b,
1640                                                     vc->vc_cols);
1641                                 ywrap_down(vc, count);
1642                                 if (t > 0)
1643                                         fbcon_bmove(vc, count, 0, 0, 0, t,
1644                                                     vc->vc_cols);
1645                         } else if (info->flags & FBINFO_READS_FAST)
1646                                 fbcon_bmove(vc, t, 0, t + count, 0,
1647                                             b - t - count, vc->vc_cols);
1648                         else
1649                                 goto redraw_down;
1650                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
1651                         break;
1652
1653                 case SCROLL_PAN_MOVE:
1654                         if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1655                             && ((!scroll_partial && (b - t == vc->vc_rows))
1656                                 || (scroll_partial
1657                                     && (b - t - count >
1658                                         3 * vc->vc_rows >> 2)))) {
1659                                 if (vc->vc_rows - b > 0)
1660                                         fbcon_bmove(vc, b, 0, b - count, 0,
1661                                                     vc->vc_rows - b,
1662                                                     vc->vc_cols);
1663                                 ypan_down(vc, count);
1664                                 if (t > 0)
1665                                         fbcon_bmove(vc, count, 0, 0, 0, t,
1666                                                     vc->vc_cols);
1667                         } else if (info->flags & FBINFO_READS_FAST)
1668                                 fbcon_bmove(vc, t, 0, t + count, 0,
1669                                             b - t - count, vc->vc_cols);
1670                         else
1671                                 goto redraw_down;
1672                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
1673                         break;
1674
1675                 case SCROLL_PAN_REDRAW:
1676                         if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1677                             && ((!scroll_partial && (b - t == vc->vc_rows))
1678                                 || (scroll_partial
1679                                     && (b - t - count >
1680                                         3 * vc->vc_rows >> 2)))) {
1681                                 if (vc->vc_rows - b > 0)
1682                                         fbcon_redraw_move(vc, p, b, vc->vc_rows - b,
1683                                                           b - count);
1684                                 ypan_down_redraw(vc, t, count);
1685                                 if (t > 0)
1686                                         fbcon_redraw_move(vc, p, count, t, 0);
1687                         } else
1688                                 fbcon_redraw_move(vc, p, t, b - t - count, t + count);
1689                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
1690                         break;
1691
1692                 case SCROLL_REDRAW:
1693                       redraw_down:
1694                         fbcon_redraw(vc, p, b - 1, b - t - count,
1695                                      -count * vc->vc_cols);
1696                         accel_clear(vc, info, real_y(p, t), 0, count,
1697                                          vc->vc_cols);
1698                         scr_memsetw((unsigned short *) (vc->vc_origin +
1699                                                         vc->vc_size_row *
1700                                                         t),
1701                                     vc->vc_video_erase_char,
1702                                     vc->vc_size_row * count);
1703                         return 1;
1704                 }
1705         }
1706         return 0;
1707 }
1708
1709
1710 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
1711                         int height, int width)
1712 {
1713         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1714         struct display *p = &fb_display[vc->vc_num];
1715         
1716         if (!info->fbops->fb_blank && console_blanked)
1717                 return;
1718
1719         if (!width || !height)
1720                 return;
1721
1722         /*  Split blits that cross physical y_wrap case.
1723          *  Pathological case involves 4 blits, better to use recursive
1724          *  code rather than unrolled case
1725          *
1726          *  Recursive invocations don't need to erase the cursor over and
1727          *  over again, so we use fbcon_bmove_rec()
1728          */
1729         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
1730                         p->vrows - p->yscroll);
1731 }
1732
1733 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx, 
1734                             int dy, int dx, int height, int width, u_int y_break)
1735 {
1736         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1737         u_int b;
1738
1739         if (sy < y_break && sy + height > y_break) {
1740                 b = y_break - sy;
1741                 if (dy < sy) {  /* Avoid trashing self */
1742                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1743                                         y_break);
1744                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1745                                         height - b, width, y_break);
1746                 } else {
1747                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1748                                         height - b, width, y_break);
1749                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1750                                         y_break);
1751                 }
1752                 return;
1753         }
1754
1755         if (dy < y_break && dy + height > y_break) {
1756                 b = y_break - dy;
1757                 if (dy < sy) {  /* Avoid trashing self */
1758                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1759                                         y_break);
1760                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1761                                         height - b, width, y_break);
1762                 } else {
1763                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1764                                         height - b, width, y_break);
1765                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1766                                         y_break);
1767                 }
1768                 return;
1769         }
1770         accel_bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
1771                         height, width);
1772 }
1773
1774 static __inline__ void updatescrollmode(struct display *p, struct fb_info *info,
1775                                         struct vc_data *vc)
1776 {
1777         int fh = vc->vc_font.height;
1778         int cap = info->flags;
1779         int good_pan = (cap & FBINFO_HWACCEL_YPAN)
1780                  && divides(info->fix.ypanstep, vc->vc_font.height)
1781                  && info->var.yres_virtual > info->var.yres;
1782         int good_wrap = (cap & FBINFO_HWACCEL_YWRAP)
1783                  && divides(info->fix.ywrapstep, vc->vc_font.height)
1784                  && divides(vc->vc_font.height, info->var.yres_virtual);
1785         int reading_fast = cap & FBINFO_READS_FAST;
1786         int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) && !(cap & FBINFO_HWACCEL_DISABLED);
1787         int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) && !(cap & FBINFO_HWACCEL_DISABLED);
1788
1789         p->vrows = info->var.yres_virtual/fh;
1790         if (info->var.yres > (fh * (vc->vc_rows + 1)))
1791                 p->vrows -= (info->var.yres - (fh * vc->vc_rows)) / fh;
1792         if ((info->var.yres % fh) && (info->var.yres_virtual % fh <
1793                                       info->var.yres % fh))
1794                 p->vrows--;
1795
1796         if (good_wrap || good_pan) {
1797                 if (reading_fast || fast_copyarea)
1798                         p->scrollmode = good_wrap ? SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
1799                 else
1800                         p->scrollmode = good_wrap ? SCROLL_REDRAW :
1801                                 SCROLL_PAN_REDRAW;
1802         } else {
1803                 if (reading_fast || (fast_copyarea && !fast_imageblit))
1804                         p->scrollmode = SCROLL_MOVE;
1805                 else
1806                         p->scrollmode = SCROLL_REDRAW;
1807         }
1808 }
1809
1810 static int fbcon_resize(struct vc_data *vc, unsigned int width, 
1811                         unsigned int height)
1812 {
1813         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1814         struct display *p = &fb_display[vc->vc_num];
1815         struct fb_var_screeninfo var = info->var;
1816         int err; int x_diff, y_diff;
1817         int fw = vc->vc_font.width;
1818         int fh = vc->vc_font.height;
1819
1820         var.xres = width * fw;
1821         var.yres = height * fh;
1822         x_diff = info->var.xres - var.xres;
1823         y_diff = info->var.yres - var.yres;
1824         if (x_diff < 0 || x_diff > fw || (y_diff < 0 || y_diff > fh)) {
1825                 char mode[40];
1826
1827                 DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
1828                 snprintf(mode, 40, "%ix%i", var.xres, var.yres);
1829                 err = fb_find_mode(&var, info, mode, info->monspecs.modedb,
1830                                    info->monspecs.modedb_len, NULL,
1831                                    info->var.bits_per_pixel);
1832                 if (!err || width > var.xres/fw || height > var.yres/fh)
1833                         return -EINVAL;
1834                 DPRINTK("resize now %ix%i\n", var.xres, var.yres);
1835                 if (CON_IS_VISIBLE(vc)) {
1836                         var.activate = FB_ACTIVATE_NOW |
1837                                 FB_ACTIVATE_FORCE;
1838                         fb_set_var(info, &var);
1839                         info->flags &= ~FBINFO_MISC_MODESWITCH;
1840                 }
1841         }
1842         updatescrollmode(p, info, vc);
1843         return 0;
1844 }
1845
1846 static int fbcon_switch(struct vc_data *vc)
1847 {
1848         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1849         struct display *p = &fb_display[vc->vc_num];
1850         int i;
1851
1852         if (softback_top) {
1853                 int l = fbcon_softback_size / vc->vc_size_row;
1854                 if (softback_lines)
1855                         fbcon_set_origin(vc);
1856                 softback_top = softback_curr = softback_in = softback_buf;
1857                 softback_lines = 0;
1858
1859                 if (l > 5)
1860                         softback_end = softback_buf + l * vc->vc_size_row;
1861                 else {
1862                         /* Smaller scrollback makes no sense, and 0 would screw
1863                            the operation totally */
1864                         softback_top = 0;
1865                 }
1866         }
1867         if (logo_shown >= 0) {
1868                 struct vc_data *conp2 = vc_cons[logo_shown].d;
1869
1870                 if (conp2->vc_top == logo_lines
1871                     && conp2->vc_bottom == conp2->vc_rows)
1872                         conp2->vc_top = 0;
1873                 logo_shown = -1;
1874         }
1875         if (info)
1876                 info->var.yoffset = p->yscroll = 0;
1877
1878         /*
1879          * FIXME: If we have multiple fbdev's loaded, we need to
1880          * update all info->currcon.  Perhaps, we can place this
1881          * in a centralized structure, but this might break some
1882          * drivers.
1883          *
1884          * info->currcon = vc->vc_num;
1885          */
1886         for (i = 0; i < FB_MAX; i++) {
1887                 if (registered_fb[i] != NULL)
1888                         registered_fb[i]->currcon = vc->vc_num;
1889         }
1890
1891         fbcon_resize(vc, vc->vc_cols, vc->vc_rows);
1892
1893         if (info->flags & FBINFO_MISC_MODESWITCH) {
1894                 if (info->fbops->fb_set_par)
1895                         info->fbops->fb_set_par(info);
1896                 info->flags &= ~FBINFO_MISC_MODESWITCH;
1897         }
1898
1899         switch (p->scrollmode) {
1900         case SCROLL_WRAP_MOVE:
1901                 scrollback_phys_max = p->vrows - vc->vc_rows;
1902                 break;
1903         case SCROLL_PAN_MOVE:
1904         case SCROLL_PAN_REDRAW:
1905                 scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
1906                 if (scrollback_phys_max < 0)
1907                         scrollback_phys_max = 0;
1908                 break;
1909         default:
1910                 scrollback_phys_max = 0;
1911                 break;
1912         }
1913         scrollback_max = 0;
1914         scrollback_current = 0;
1915
1916         update_var(vc->vc_num, info);
1917         fbcon_set_palette(vc, color_table);     
1918
1919         if (vt_cons[vc->vc_num]->vc_mode == KD_TEXT)
1920                 accel_clear_margins(vc, info, 0);
1921         if (logo_shown == -2) {
1922                 logo_shown = fg_console;
1923                 /* This is protected above by initmem_freed */
1924                 fb_show_logo(info);
1925                 update_region(fg_console,
1926                               vc->vc_origin + vc->vc_size_row * vc->vc_top,
1927                               vc->vc_size_row * (vc->vc_bottom -
1928                                                  vc->vc_top) / 2);
1929                 return 0;
1930         }
1931         return 1;
1932 }
1933
1934 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
1935 {
1936         unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
1937         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1938         struct display *p = &fb_display[vc->vc_num];
1939
1940         if (mode_switch) {
1941                 struct fb_var_screeninfo var = info->var;
1942
1943 /*
1944  * HACK ALERT: Some hardware will require reinitializion at this stage,
1945  *             others will require it to be done as late as possible.
1946  *             For now, we differentiate this with the
1947  *             FBINFO_MISC_MODESWITCHLATE bitflag.  Worst case will be
1948  *             hardware that requires it here and another one later.
1949  *             A definitive solution may require fixing X or the VT
1950  *             system.
1951  */
1952                 if (info->flags & FBINFO_MISC_MODESWITCHLATE)
1953                         info->flags |= FBINFO_MISC_MODESWITCH;
1954
1955                 if (blank) {
1956                         fbcon_cursor(vc, CM_ERASE);
1957                         return 0;
1958                 }
1959
1960                 if (!(info->flags & FBINFO_MISC_MODESWITCHLATE)) {
1961                         var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
1962                         fb_set_var(info, &var);
1963                 }
1964         }
1965
1966         fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
1967
1968         if (!info->fbops->fb_blank) {
1969                 if (blank) {
1970                         unsigned short oldc;
1971                         u_int height;
1972                         u_int y_break;
1973
1974                         oldc = vc->vc_video_erase_char;
1975                         vc->vc_video_erase_char &= charmask;
1976                         height = vc->vc_rows;
1977                         y_break = p->vrows - p->yscroll;
1978                         if (height > y_break) {
1979                                 accel_clear(vc, info, real_y(p, 0),
1980                                             0, y_break, vc->vc_cols);
1981                                 accel_clear(vc, info, real_y(p, y_break),
1982                                             0, height - y_break, 
1983                                             vc->vc_cols);
1984                         } else
1985                                 accel_clear(vc, info, real_y(p, 0),
1986                                             0, height, vc->vc_cols);
1987                         vc->vc_video_erase_char = oldc;
1988                 } else
1989                         update_screen(vc->vc_num);
1990                 return 0;
1991         } else
1992                 return fb_blank(info, blank);
1993 }
1994
1995 static void fbcon_free_font(struct display *p)
1996 {
1997         if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
1998                 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
1999         p->fontdata = NULL;
2000         p->userfont = 0;
2001 }
2002
2003 static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
2004 {
2005         u8 *fontdata = vc->vc_font.data;
2006         u8 *data = font->data;
2007         int i, j;
2008
2009         font->width = vc->vc_font.width;
2010         font->height = vc->vc_font.height;
2011         font->charcount = vc->vc_hi_font_mask ? 512 : 256;
2012         if (!font->data)
2013                 return 0;
2014
2015         if (font->width <= 8) {
2016                 j = vc->vc_font.height;
2017                 for (i = 0; i < font->charcount; i++) {
2018                         memcpy(data, fontdata, j);
2019                         memset(data + j, 0, 32 - j);
2020                         data += 32;
2021                         fontdata += j;
2022                 }
2023         } else if (font->width <= 16) {
2024                 j = vc->vc_font.height * 2;
2025                 for (i = 0; i < font->charcount; i++) {
2026                         memcpy(data, fontdata, j);
2027                         memset(data + j, 0, 64 - j);
2028                         data += 64;
2029                         fontdata += j;
2030                 }
2031         } else if (font->width <= 24) {
2032                 for (i = 0; i < font->charcount; i++) {
2033                         for (j = 0; j < vc->vc_font.height; j++) {
2034                                 *data++ = fontdata[0];
2035                                 *data++ = fontdata[1];
2036                                 *data++ = fontdata[2];
2037                                 fontdata += sizeof(u32);
2038                         }
2039                         memset(data, 0, 3 * (32 - j));
2040                         data += 3 * (32 - j);
2041                 }
2042         } else {
2043                 j = vc->vc_font.height * 4;
2044                 for (i = 0; i < font->charcount; i++) {
2045                         memcpy(data, fontdata, j);
2046                         memset(data + j, 0, 128 - j);
2047                         data += 128;
2048                         fontdata += j;
2049                 }
2050         }
2051         return 0;
2052 }
2053
2054 static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
2055                              u8 * data, int userfont)
2056 {
2057         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2058         struct display *p = &fb_display[vc->vc_num];
2059         int resize;
2060         int cnt;
2061         char *old_data = NULL;
2062
2063         if (CON_IS_VISIBLE(vc) && softback_lines)
2064                 fbcon_set_origin(vc);
2065
2066         resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
2067         if (p->userfont)
2068                 old_data = vc->vc_font.data;
2069         if (userfont)
2070                 cnt = FNTCHARCNT(data);
2071         else
2072                 cnt = 256;
2073         vc->vc_font.data = p->fontdata = data;
2074         if ((p->userfont = userfont))
2075                 REFCOUNT(data)++;
2076         vc->vc_font.width = w;
2077         vc->vc_font.height = h;
2078         if (vc->vc_hi_font_mask && cnt == 256) {
2079                 vc->vc_hi_font_mask = 0;
2080                 if (vc->vc_can_do_color) {
2081                         vc->vc_complement_mask >>= 1;
2082                         vc->vc_s_complement_mask >>= 1;
2083                 }
2084                         
2085                 /* ++Edmund: reorder the attribute bits */
2086                 if (vc->vc_can_do_color) {
2087                         unsigned short *cp =
2088                             (unsigned short *) vc->vc_origin;
2089                         int count = vc->vc_screenbuf_size / 2;
2090                         unsigned short c;
2091                         for (; count > 0; count--, cp++) {
2092                                 c = scr_readw(cp);
2093                                 scr_writew(((c & 0xfe00) >> 1) |
2094                                            (c & 0xff), cp);
2095                         }
2096                         c = vc->vc_video_erase_char;
2097                         vc->vc_video_erase_char =
2098                             ((c & 0xfe00) >> 1) | (c & 0xff);
2099                         vc->vc_attr >>= 1;
2100                 }
2101         } else if (!vc->vc_hi_font_mask && cnt == 512) {
2102                 vc->vc_hi_font_mask = 0x100;
2103                 if (vc->vc_can_do_color) {
2104                         vc->vc_complement_mask <<= 1;
2105                         vc->vc_s_complement_mask <<= 1;
2106                 }
2107                         
2108                 /* ++Edmund: reorder the attribute bits */
2109                 {
2110                         unsigned short *cp =
2111                             (unsigned short *) vc->vc_origin;
2112                         int count = vc->vc_screenbuf_size / 2;
2113                         unsigned short c;
2114                         for (; count > 0; count--, cp++) {
2115                                 unsigned short newc;
2116                                 c = scr_readw(cp);
2117                                 if (vc->vc_can_do_color)
2118                                         newc =
2119                                             ((c & 0xff00) << 1) | (c &
2120                                                                    0xff);
2121                                 else
2122                                         newc = c & ~0x100;
2123                                 scr_writew(newc, cp);
2124                         }
2125                         c = vc->vc_video_erase_char;
2126                         if (vc->vc_can_do_color) {
2127                                 vc->vc_video_erase_char =
2128                                     ((c & 0xff00) << 1) | (c & 0xff);
2129                                 vc->vc_attr <<= 1;
2130                         } else
2131                                 vc->vc_video_erase_char = c & ~0x100;
2132                 }
2133
2134         }
2135
2136         if (resize) {
2137                 /* reset wrap/pan */
2138                 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
2139                 vc_resize(vc->vc_num, info->var.xres / w, info->var.yres / h);
2140                 if (CON_IS_VISIBLE(vc) && softback_buf) {
2141                         int l = fbcon_softback_size / vc->vc_size_row;
2142                         if (l > 5)
2143                                 softback_end =
2144                                     softback_buf + l * vc->vc_size_row;
2145                         else {
2146                                 /* Smaller scrollback makes no sense, and 0 would screw
2147                                    the operation totally */
2148                                 softback_top = 0;
2149                         }
2150                 }
2151         } else if (CON_IS_VISIBLE(vc)
2152                    && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
2153                 accel_clear_margins(vc, info, 0);
2154                 update_screen(vc->vc_num);
2155         }
2156
2157         if (old_data && (--REFCOUNT(old_data) == 0))
2158                 kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
2159         return 0;
2160 }
2161
2162 static int fbcon_copy_font(struct vc_data *vc, int con)
2163 {
2164         struct display *od = &fb_display[con];
2165         struct console_font *f = &vc->vc_font;
2166
2167         if (od->fontdata == f->data)
2168                 return 0;       /* already the same font... */
2169         return fbcon_do_set_font(vc, f->width, f->height, od->fontdata, od->userfont);
2170 }
2171
2172 /*
2173  *  User asked to set font; we are guaranteed that
2174  *      a) width and height are in range 1..32
2175  *      b) charcount does not exceed 512
2176  */
2177
2178 static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags)
2179 {
2180         unsigned charcount = font->charcount;
2181         int w = font->width;
2182         int h = font->height;
2183         int size = h;
2184         int i, k;
2185         u8 *new_data, *data = font->data, *p;
2186
2187         if (charcount != 256 && charcount != 512)
2188                 return -EINVAL;
2189
2190         if (w > 8) {
2191                 if (w <= 16)
2192                         size *= 2;
2193                 else
2194                         size *= 4;
2195         }
2196         size *= charcount;
2197
2198         new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER);
2199
2200         if (!new_data)
2201                 return -ENOMEM;
2202
2203         new_data += FONT_EXTRA_WORDS * sizeof(int);
2204         FNTSIZE(new_data) = size;
2205         FNTCHARCNT(new_data) = charcount;
2206         REFCOUNT(new_data) = 0; /* usage counter */
2207         p = new_data;
2208         if (w <= 8) {
2209                 for (i = 0; i < charcount; i++) {
2210                         memcpy(p, data, h);
2211                         data += 32;
2212                         p += h;
2213                 }
2214         } else if (w <= 16) {
2215                 h *= 2;
2216                 for (i = 0; i < charcount; i++) {
2217                         memcpy(p, data, h);
2218                         data += 64;
2219                         p += h;
2220                 }
2221         } else if (w <= 24) {
2222                 for (i = 0; i < charcount; i++) {
2223                         int j;
2224                         for (j = 0; j < h; j++) {
2225                                 memcpy(p, data, 3);
2226                                 p[3] = 0;
2227                                 data += 3;
2228                                 p += sizeof(u32);
2229                         }
2230                         data += 3 * (32 - h);
2231                 }
2232         } else {
2233                 h *= 4;
2234                 for (i = 0; i < charcount; i++) {
2235                         memcpy(p, data, h);
2236                         data += 128;
2237                         p += h;
2238                 }
2239         }
2240         /* we can do it in u32 chunks because of charcount is 256 or 512, so
2241            font length must be multiple of 256, at least. And 256 is multiple
2242            of 4 */
2243         k = 0;
2244         while (p > new_data) {
2245                 p = (u8 *)((u32 *)p - 1);
2246                 k += *(u32 *) p;
2247         }
2248         FNTSUM(new_data) = k;
2249         /* Check if the same font is on some other console already */
2250         for (i = 0; i < MAX_NR_CONSOLES; i++) {
2251                 struct vc_data *tmp = vc_cons[i].d;
2252                 
2253                 if (fb_display[i].userfont &&
2254                     fb_display[i].fontdata &&
2255                     FNTSUM(fb_display[i].fontdata) == k &&
2256                     FNTSIZE(fb_display[i].fontdata) == size &&
2257                     tmp->vc_font.width == w &&
2258                     !memcmp(fb_display[i].fontdata, new_data, size)) {
2259                         kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
2260                         new_data = fb_display[i].fontdata;
2261                         break;
2262                 }
2263         }
2264         return fbcon_do_set_font(vc, font->width, font->height, new_data, 1);
2265 }
2266
2267 static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name)
2268 {
2269         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2270         struct font_desc *f;
2271
2272         if (!name)
2273                 f = get_default_font(info->var.xres, info->var.yres);
2274         else if (!(f = find_font(name)))
2275                 return -ENOENT;
2276
2277         font->width = f->width;
2278         font->height = f->height;
2279         return fbcon_do_set_font(vc, f->width, f->height, f->data, 0);
2280 }
2281
2282 static u16 palette_red[16];
2283 static u16 palette_green[16];
2284 static u16 palette_blue[16];
2285
2286 static struct fb_cmap palette_cmap = {
2287         0, 16, palette_red, palette_green, palette_blue, NULL
2288 };
2289
2290 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table)
2291 {
2292         struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2293         int i, j, k;
2294         u8 val;
2295
2296         if (!vc->vc_can_do_color
2297             || (!info->fbops->fb_blank && console_blanked))
2298                 return -EINVAL;
2299         for (i = j = 0; i < 16; i++) {
2300                 k = table[i];
2301                 val = vc->vc_palette[j++];
2302                 palette_red[k] = (val << 8) | val;
2303                 val = vc->vc_palette[j++];
2304                 palette_green[k] = (val << 8) | val;
2305                 val = vc->vc_palette[j++];
2306                 palette_blue[k] = (val << 8) | val;
2307         }
2308         if (info->var.bits_per_pixel <= 4)
2309                 palette_cmap.len = 1 << info->var.bits_per_pixel;
2310         else
2311                 palette_cmap.len = 16;
2312         palette_cmap.start = 0;
2313         return fb_set_cmap(&palette_cmap, info);
2314 }
2315
2316 static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
2317 {
2318         unsigned long p;
2319         int line;
2320         
2321         if (vc->vc_num != fg_console || !softback_lines)
2322                 return (u16 *) (vc->vc_origin + offset);
2323         line = offset / vc->vc_size_row;
2324         if (line >= softback_lines)
2325                 return (u16 *) (vc->vc_origin + offset -
2326                                 softback_lines * vc->vc_size_row);
2327         p = softback_curr + offset;
2328         if (p >= softback_end)
2329                 p += softback_buf - softback_end;
2330         return (u16 *) p;
2331 }
2332
2333 static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
2334                                  int *px, int *py)
2335 {
2336         unsigned long ret;
2337         int x, y;
2338
2339         if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
2340                 unsigned long offset = (pos - vc->vc_origin) / 2;
2341
2342                 x = offset % vc->vc_cols;
2343                 y = offset / vc->vc_cols;
2344                 if (vc->vc_num == fg_console)
2345                         y += softback_lines;
2346                 ret = pos + (vc->vc_cols - x) * 2;
2347         } else if (vc->vc_num == fg_console && softback_lines) {
2348                 unsigned long offset = pos - softback_curr;
2349
2350                 if (pos < softback_curr)
2351                         offset += softback_end - softback_buf;
2352                 offset /= 2;
2353                 x = offset % vc->vc_cols;
2354                 y = offset / vc->vc_cols;
2355                 ret = pos + (vc->vc_cols - x) * 2;
2356                 if (ret == softback_end)
2357                         ret = softback_buf;
2358                 if (ret == softback_in)
2359                         ret = vc->vc_origin;
2360         } else {
2361                 /* Should not happen */
2362                 x = y = 0;
2363                 ret = vc->vc_origin;
2364         }
2365         if (px)
2366                 *px = x;
2367         if (py)
2368                 *py = y;
2369         return ret;
2370 }
2371
2372 /* As we might be inside of softback, we may work with non-contiguous buffer,
2373    that's why we have to use a separate routine. */
2374 static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
2375 {
2376         while (cnt--) {
2377                 u16 a = scr_readw(p);
2378                 if (!vc->vc_can_do_color)
2379                         a ^= 0x0800;
2380                 else if (vc->vc_hi_font_mask == 0x100)
2381                         a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
2382                             (((a) & 0x0e00) << 4);
2383                 else
2384                         a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
2385                             (((a) & 0x0700) << 4);
2386                 scr_writew(a, p++);
2387                 if (p == (u16 *) softback_end)
2388                         p = (u16 *) softback_buf;
2389                 if (p == (u16 *) softback_in)
2390                         p = (u16 *) vc->vc_origin;
2391         }
2392 }
2393
2394 static int fbcon_scrolldelta(struct vc_data *vc, int lines)
2395 {
2396         struct fb_info *info = registered_fb[(int) con2fb_map[fg_console]];
2397         struct display *p = &fb_display[fg_console];
2398         int offset, limit, scrollback_old;
2399
2400         if (softback_top) {
2401                 if (vc->vc_num != fg_console)
2402                         return 0;
2403                 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT || !lines)
2404                         return 0;
2405                 if (logo_shown >= 0) {
2406                         struct vc_data *conp2 = vc_cons[logo_shown].d;
2407
2408                         if (conp2->vc_top == logo_lines
2409                             && conp2->vc_bottom == conp2->vc_rows)
2410                                 conp2->vc_top = 0;
2411                         if (logo_shown == vc->vc_num) {
2412                                 unsigned long p, q;
2413                                 int i;
2414
2415                                 p = softback_in;
2416                                 q = vc->vc_origin +
2417                                     logo_lines * vc->vc_size_row;
2418                                 for (i = 0; i < logo_lines; i++) {
2419                                         if (p == softback_top)
2420                                                 break;
2421                                         if (p == softback_buf)
2422                                                 p = softback_end;
2423                                         p -= vc->vc_size_row;
2424                                         q -= vc->vc_size_row;
2425                                         scr_memcpyw((u16 *) q, (u16 *) p,
2426                                                     vc->vc_size_row);
2427                                 }
2428                                 softback_in = p;
2429                                 update_region(vc->vc_num, vc->vc_origin,
2430                                               logo_lines * vc->vc_cols);
2431                         }
2432                         logo_shown = -1;
2433                 }
2434                 fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK);
2435                 fbcon_redraw_softback(vc, p, lines);
2436                 fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK);
2437                 return 0;
2438         }
2439
2440         if (!scrollback_phys_max)
2441                 return -ENOSYS;
2442
2443         scrollback_old = scrollback_current;
2444         scrollback_current -= lines;
2445         if (scrollback_current < 0)
2446                 scrollback_current = 0;
2447         else if (scrollback_current > scrollback_max)
2448                 scrollback_current = scrollback_max;
2449         if (scrollback_current == scrollback_old)
2450                 return 0;
2451
2452         if (!info->fbops->fb_blank &&
2453             (console_blanked || vt_cons[vc->vc_num]->vc_mode != KD_TEXT
2454              || !lines))
2455                 return 0;
2456         fbcon_cursor(vc, CM_ERASE);
2457
2458         offset = p->yscroll - scrollback_current;
2459         limit = p->vrows;
2460         switch (p->scrollmode) {
2461         case SCROLL_WRAP_MOVE:
2462                 info->var.vmode |= FB_VMODE_YWRAP;
2463                 break;
2464         case SCROLL_PAN_MOVE:
2465         case SCROLL_PAN_REDRAW:
2466                 limit -= vc->vc_rows;
2467                 info->var.vmode &= ~FB_VMODE_YWRAP;
2468                 break;
2469         }
2470         if (offset < 0)
2471                 offset += limit;
2472         else if (offset >= limit)
2473                 offset -= limit;
2474         info->var.xoffset = 0;
2475         info->var.yoffset = offset * vc->vc_font.height;
2476         update_var(vc->vc_num, info);
2477         if (!scrollback_current)
2478                 fbcon_cursor(vc, CM_DRAW);
2479         return 0;
2480 }
2481
2482 static int fbcon_set_origin(struct vc_data *vc)
2483 {
2484         if (softback_lines && !console_blanked)
2485                 fbcon_scrolldelta(vc, softback_lines);
2486         return 0;
2487 }
2488
2489 static void fbcon_suspended(struct fb_info *info)
2490 {
2491         /* Clear cursor, restore saved data */
2492         info->cursor.enable = 0;
2493         info->fbops->fb_cursor(info, &info->cursor);
2494 }
2495
2496 static void fbcon_resumed(struct fb_info *info)
2497 {
2498         struct vc_data *vc;
2499
2500         if (info->currcon < 0)
2501                 return;
2502         vc = vc_cons[info->currcon].d;
2503
2504         update_screen(vc->vc_num);
2505 }
2506
2507 static void fbcon_modechanged(struct fb_info *info)
2508 {
2509         struct vc_data *vc = vc_cons[info->currcon].d;
2510         struct display *p;
2511         int rows, cols;
2512
2513         if (info->currcon < 0 || vt_cons[info->currcon]->vc_mode !=
2514             KD_TEXT)
2515                 return;
2516         p = &fb_display[vc->vc_num];
2517
2518         info->var.xoffset = info->var.yoffset = p->yscroll = 0;
2519         vc->vc_can_do_color = info->var.bits_per_pixel != 1;
2520         vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
2521
2522         if (CON_IS_VISIBLE(vc)) {
2523                 cols = info->var.xres / vc->vc_font.width;
2524                 rows = info->var.yres / vc->vc_font.height;
2525                 vc_resize(vc->vc_num, cols, rows);
2526                 updatescrollmode(p, info, vc);
2527                 scrollback_max = 0;
2528                 scrollback_current = 0;
2529                 update_var(vc->vc_num, info);
2530                 fbcon_set_palette(vc, color_table);
2531                 update_screen(vc->vc_num);
2532                 if (softback_buf) {
2533                         int l = fbcon_softback_size / vc->vc_size_row;
2534                         if (l > 5)
2535                                 softback_end = softback_buf + l * vc->vc_size_row;
2536                         else {
2537                                 /* Smaller scrollback makes no sense, and 0
2538                                    would screw the operation totally */
2539                                 softback_top = 0;
2540                         }
2541                 }
2542         }
2543 }
2544
2545 static int fbcon_event_notify(struct notifier_block *self, 
2546                               unsigned long action, void *data)
2547 {
2548         struct fb_info *info = (struct fb_info *) data;
2549
2550         switch(action) {
2551         case FB_EVENT_SUSPEND:
2552                 fbcon_suspended(info);
2553                 break;
2554         case FB_EVENT_RESUME:
2555                 fbcon_resumed(info);
2556                 break;
2557         case FB_EVENT_MODE_CHANGE:
2558                 fbcon_modechanged(info);
2559                 break;
2560         }
2561
2562         return 0;
2563 }
2564
2565 /*
2566  *  The console `switch' structure for the frame buffer based console
2567  */
2568
2569 const struct consw fb_con = {
2570         .owner                  = THIS_MODULE,
2571         .con_startup            = fbcon_startup,
2572         .con_init               = fbcon_init,
2573         .con_deinit             = fbcon_deinit,
2574         .con_clear              = fbcon_clear,
2575         .con_putc               = fbcon_putc,
2576         .con_putcs              = fbcon_putcs,
2577         .con_cursor             = fbcon_cursor,
2578         .con_scroll             = fbcon_scroll,
2579         .con_bmove              = fbcon_bmove,
2580         .con_switch             = fbcon_switch,
2581         .con_blank              = fbcon_blank,
2582         .con_font_set           = fbcon_set_font,
2583         .con_font_get           = fbcon_get_font,
2584         .con_font_default       = fbcon_set_def_font,
2585         .con_font_copy          = fbcon_copy_font,
2586         .con_set_palette        = fbcon_set_palette,
2587         .con_scrolldelta        = fbcon_scrolldelta,
2588         .con_set_origin         = fbcon_set_origin,
2589         .con_invert_region      = fbcon_invert_region,
2590         .con_screen_pos         = fbcon_screen_pos,
2591         .con_getxy              = fbcon_getxy,
2592         .con_resize             = fbcon_resize,
2593 };
2594
2595 static struct notifier_block fbcon_event_notifier = {
2596         .notifier_call  = fbcon_event_notify,
2597 };
2598 static int fbcon_event_notifier_registered;
2599
2600 /* can't be __init as it can be called by set_con2fb_map() later */
2601 int fb_console_init(void)
2602 {
2603         int err, i;
2604
2605         for (i = 0; i < MAX_NR_CONSOLES; i++)
2606                 con2fb_map[i] = -1;
2607
2608         if (!num_registered_fb)
2609                 return -ENODEV;
2610
2611         if (info_idx == -1) {
2612                 for (i = 0; i < FB_MAX; i++) {
2613                         if (registered_fb[i] != NULL) {
2614                                 info_idx = i;
2615                                 break;
2616                         }
2617                 }
2618         }
2619         for (i = first_fb_vc; i <= last_fb_vc; i++)
2620                 con2fb_map[i] = info_idx;
2621         err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
2622                                 fbcon_is_default);
2623         if (err) {
2624                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2625                         con2fb_map[i] = -1;
2626                 }
2627                 return err;
2628         }
2629         acquire_console_sem();
2630         if (!fbcon_event_notifier_registered) {
2631                 fb_register_client(&fbcon_event_notifier);
2632                 fbcon_event_notifier_registered = 1;
2633         } 
2634         release_console_sem();
2635         return 0;
2636 }
2637
2638 #ifdef MODULE
2639
2640 void __exit fb_console_exit(void)
2641 {
2642         acquire_console_sem();
2643         if (fbcon_event_notifier_registered) {
2644                 fb_unregister_client(&fbcon_event_notifier);
2645                 fbcon_event_notifier_registered = 0;
2646         }
2647         release_console_sem();
2648         give_up_console(&fb_con);
2649 }       
2650
2651 module_init(fb_console_init);
2652 module_exit(fb_console_exit);
2653
2654 #endif
2655
2656 /*
2657  *  Visible symbols for modules
2658  */
2659
2660 EXPORT_SYMBOL(fb_display);
2661 EXPORT_SYMBOL(fb_con);
2662
2663 MODULE_LICENSE("GPL");