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