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