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