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