patch-2.1.109 linux/drivers/video/fbcon.c

Next file: linux/drivers/video/fbcon.h
Previous file: linux/drivers/video/fbcon-vga.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon.c linux/drivers/video/fbcon.c
@@ -25,6 +25,7 @@
  *			   Andreas Schwab
  *
  *  Hardware cursor support added by Emmanuel Marty (core@ggi-project.org)
+ *  Smart redraw scrolling added by Jakub Jelinek (jj@ultra.linux.cz)
  *
  *
  *  The low level operations for the various display memory organizations are
@@ -37,6 +38,7 @@
  *    o ilbm			Amiga interleaved bitplanes
  *    o iplan2p[248]		Atari interleaved bitplanes
  *    o mfb			Monochrome
+ *    o vga			VGA characters/attributes
  *
  *  To do:
  *
@@ -50,7 +52,6 @@
 
 #undef FBCONDEBUG
 
-#define SUPPORT_SCROLLBACK	0
 #define FLASHING_CURSOR		1
 
 #include <linux/config.h>
@@ -68,6 +69,7 @@
 #include <linux/fb.h>
 #include <linux/vt_kern.h>
 #include <linux/selection.h>
+#include <linux/smp.h>
 #include <linux/init.h>
 
 #include <asm/irq.h>
@@ -87,6 +89,7 @@
 #include <asm/machdep.h>
 #include <asm/setup.h>
 #endif
+#define INCLUDE_LINUX_LOGO_DATA
 #include <asm/linux_logo.h>
 
 #include "fbcon.h"
@@ -99,6 +102,10 @@
 #  define DPRINTK(fmt, args...)
 #endif
 
+#define LOGO_H			80
+#define LOGO_W			80
+#define LOGO_LINE	(LOGO_W/8)
+
 struct display fb_display[MAX_NR_CONSOLES];
 
 
@@ -131,7 +138,7 @@
     vbl_cursor_cnt = 0;
     cursor_was_drawn = cursor_drawn;
     cursor_drawn = 0;
-    return(cursor_was_drawn);
+    return cursor_was_drawn;
 }
 #endif
 
@@ -139,10 +146,6 @@
  *  Scroll Method
  */
 
-#define SCROLL_YWRAP	(0)
-#define SCROLL_YPAN	(1)
-#define SCROLL_YMOVE	(2)
-
 #define divides(a, b)	((!(a) || (b)%(a)) ? 0 : 1)
 
 
@@ -150,18 +153,17 @@
  *  Interface used by the world
  */
 
-static unsigned long fbcon_startup(unsigned long kmem_start,
-				   const char **display_desc);
-static void fbcon_init(struct vc_data *conp);
+static const char *fbcon_startup(void);
+static void fbcon_init(struct vc_data *conp, int init);
 static void fbcon_deinit(struct vc_data *conp);
 static int fbcon_changevar(int con);
 static void fbcon_clear(struct vc_data *conp, int sy, int sx, int height,
 		       int width);
 static void fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos);
-static void fbcon_putcs(struct vc_data *conp, const char *s, int count,
+static void fbcon_putcs(struct vc_data *conp, const unsigned short *s, int count,
 			int ypos, int xpos);
 static void fbcon_cursor(struct vc_data *conp, int mode);
-static void fbcon_scroll(struct vc_data *conp, int t, int b, int dir,
+static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir,
 			 int count);
 static void fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
 			int height, int width);
@@ -177,21 +179,16 @@
  *  Internal routines
  */
 
-static void fbcon_setup(int con, int setcol, int init);
+static void fbcon_setup(int con, int init, int logo);
 static __inline__ int real_y(struct display *p, int ypos);
 #if FLASHING_CURSOR
 static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp);
 #endif
 static __inline__ void updatescrollmode(struct display *p);
-#if SUPPORT_SCROLLBACK
 static __inline__ void ywrap_up(int unit, struct vc_data *conp,
 				struct display *p, int count);
 static __inline__ void ywrap_down(int unit, struct vc_data *conp,
 				  struct display *p, int count);
-#else
-static __inline__ void ywrap_up(int unit, struct display *p, int count);
-static __inline__ void ywrap_down(int unit, struct display *p, int count);
-#endif
 static __inline__ void ypan_up(int unit, struct vc_data *conp,
 			       struct display *p, int count);
 static __inline__ void ypan_down(int unit, struct vc_data *conp,
@@ -237,21 +234,21 @@
 
 static struct display_switch fbcon_dummy;
 
+/* NOTE: fbcon cannot be __initfunc: it may be called from take_over_console later */
 
-__initfunc(static unsigned long fbcon_startup(unsigned long kmem_start,
-					      const char **display_desc))
+static const char *fbcon_startup(void)
 {
+    const char *display_desc = "frame buffer device";
     int irqres = 1;
+    static int done = 0;
 
-    /* Probe all frame buffer devices */
-    kmem_start = probe_framebuffers(kmem_start);
-
-    if (!num_registered_fb) {
-            DPRINTK("no framebuffer registered\n");
-	    return kmem_start;
-    }
-
-    *display_desc = "frame buffer device";
+    /*
+     *  If num_registered_fb is zero, this is a call for the dummy part.
+     *  The frame buffer devices weren't initialized yet.
+     */
+    if (!num_registered_fb || done)
+	return display_desc;
+    done = 1;
 
 #ifdef CONFIG_AMIGA
     if (MACH_IS_AMIGA) {
@@ -307,6 +304,11 @@
     }
 #endif /* CONFIG_MAC */
 
+#if defined(__arm__) && defined(IRQ_VSYNCPULSE)
+    irqres = request_irq(IRQ_VSYNCPULSE, fbcon_vbl_handler, 0,
+			 "console/cursor", fbcon_vbl_handler);
+#endif
+
     if (irqres) {
 	cursor_blink_rate = DEFAULT_CURSOR_BLINK_RATE;
 	cursor_timer.expires = jiffies+HZ/50;
@@ -315,14 +317,11 @@
 	add_timer(&cursor_timer);
     }
 
-    if (!console_show_logo)
-	console_show_logo = fbcon_show_logo;
-
-    return kmem_start;
+    return display_desc;
 }
 
 
-static void fbcon_init(struct vc_data *conp)
+static void fbcon_init(struct vc_data *conp, int init)
 {
     int unit = conp->vc_num;
     struct fb_info *info;
@@ -339,7 +338,7 @@
 	                     fb_display[unit].var.bits_per_pixel);
     fb_display[unit].conp = conp;
     fb_display[unit].fb_info = info;
-    fbcon_setup(unit, 1, 1);
+    fbcon_setup(unit, init, !init);
 }
 
 
@@ -356,13 +355,15 @@
 static int fbcon_changevar(int con)
 {
     if (fb_display[con].conp)
-	    fbcon_setup(con, 1, 0);
-    return(0);
+	    fbcon_setup(con, 0, 0);
+    return 0;
 }
 
 
 static __inline__ void updatescrollmode(struct display *p)
 {
+    if (p->scrollmode == SCROLL_YREDRAW)
+    	return;
     if (divides(p->ywrapstep, p->fontheight) &&
 	divides(p->fontheight, p->var.yres_virtual))
 	p->scrollmode = SCROLL_YWRAP;
@@ -374,11 +375,17 @@
 }
 
 
-static void fbcon_setup(int con, int setcol, int init)
+static void fbcon_setup(int con, int init, int logo)
 {
     struct display *p = &fb_display[con];
     struct vc_data *conp = p->conp;
     int nr_rows, nr_cols;
+    int old_rows, old_cols;
+    /* Only if not module */
+    extern int initmem_freed;
+    
+    if (con != fg_console || initmem_freed || p->type == FB_TYPE_TEXT)
+    	logo = 0;
 
     p->var.xoffset = p->var.yoffset = p->yscroll = 0;  /* reset wrap/pan */
 
@@ -401,7 +408,37 @@
 	}
     }
     updatescrollmode(p);
-
+    
+    if (logo) {
+    	/* Need to make room for the logo */
+    	int logo_lines = (LOGO_H + p->fontheight - 1) / p->fontheight;
+    	unsigned short *q = (unsigned short *)(conp->vc_origin + 
+    				conp->vc_size_row * conp->vc_rows);
+	unsigned short *r;
+    
+    	for (r = q - logo_lines * conp->vc_cols; r < q; r++)
+    	    if (*r != conp->vc_video_erase_char)
+    	    	break;
+    	if (r == q) {
+    	    /* We can scroll screen down */
+    	    int cnt;
+    	    int step = logo_lines * conp->vc_cols;
+
+	    r = q - step - conp->vc_cols;
+    	    for (cnt = conp->vc_rows - logo_lines; cnt > 0; cnt--) {
+    	    	scr_memcpyw(r + step, r, conp->vc_size_row);
+    	    	r -= conp->vc_cols;
+    	    }
+    	    conp->vc_y += logo_lines;
+    	    conp->vc_pos += logo_lines * conp->vc_size_row;
+    	}
+    	scr_memsetw((unsigned short *)conp->vc_origin, conp->vc_video_erase_char, 
+    		conp->vc_size_row * logo_lines);
+    }
+    
+    old_cols = conp->vc_cols;
+    old_rows = conp->vc_rows;
+    
     nr_cols = p->var.xres/p->fontwidth;
     nr_rows = p->var.yres/p->fontheight;
     /*
@@ -423,13 +460,18 @@
     }
     p->dispsw->setup(p);
 
-    if (setcol) {
-	p->fgcol = p->var.bits_per_pixel > 2 ? 7 : (1<<p->var.bits_per_pixel)-1;
-	p->bgcol = 0;
-    }
+    p->fgcol = p->var.bits_per_pixel > 2 ? 7 : (1<<p->var.bits_per_pixel)-1;
+    p->bgcol = 0;
 
-    if (!init)
+    if (!init) {
 	vc_resize_con(nr_rows, nr_cols, con);
+	if (con == fg_console &&
+	    old_rows == nr_rows && old_cols == nr_cols)
+	    update_screen(con); /* So that we set origin correctly */
+    }
+	
+    if (logo)
+    	fbcon_show_logo(); /* This is protected above by initmem_freed */
 }
 
 
@@ -463,7 +505,7 @@
     int rows = p->vrows;
 
     ypos += p->yscroll;
-    return(ypos < rows ? ypos : ypos-rows);
+    return ypos < rows ? ypos : ypos-rows;
 }
 
 
@@ -511,7 +553,7 @@
 }
 
 
-static void fbcon_putcs(struct vc_data *conp, const char *s, int count,
+static void fbcon_putcs(struct vc_data *conp, const unsigned short *s, int count,
 		       int ypos, int xpos)
 {
     int unit = conp->vc_num;
@@ -545,18 +587,21 @@
 	(mode == CM_ERASE) == !cursor_on)
 	return;
 
-	if (CURSOR_UNDRAWN ())
+	cursor_on = 0;
+	if (cursor_drawn)
 	    p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));
+
 	p->cursor_x = conp->vc_x;
 	p->cursor_y = conp->vc_y;
 
 	switch (mode) {
 	    case CM_ERASE:
-		cursor_on = 0;
-		break;
-
+	        cursor_drawn = 0;
+	        break;
 	    case CM_MOVE:
 	    case CM_DRAW:
+		if (cursor_drawn)
+		    p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));
 		vbl_cursor_cnt = CURSOR_DRAW_DELAY;
 		cursor_on = 1;
 		break;
@@ -568,33 +613,29 @@
 static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp)
 {
     struct display *p;
+    static int _vbl_cursor_cnt = 1, _vbl_cursor_drawn;
 
-    if (!cursor_on)
-	return;
+    if (!--_vbl_cursor_cnt) {
+	_vbl_cursor_cnt = cursor_blink_rate;
+	_vbl_cursor_drawn = !_vbl_cursor_drawn;
+    }
 
-    if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
-	/* Here no check is possible for console changing. The console
-	 * switching code should set vbl_cursor_cnt to an appropriate value.
-	 */
-	p = &fb_display[fg_console];
-	p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));
-	cursor_drawn ^= 1;
-	vbl_cursor_cnt = cursor_blink_rate;
+    if (cursor_on && vbl_cursor_cnt) {
+	if (cursor_drawn != _vbl_cursor_drawn) {
+	    p = &fb_display[fg_console];
+	    p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));
+	}
+	cursor_drawn = _vbl_cursor_drawn;
     }
 }
 #endif
 
-#if SUPPORT_SCROLLBACK
+static int scrollback_phys_max = 0;
 static int scrollback_max = 0;
 static int scrollback_current = 0;
-#endif
 
-#if SUPPORT_SCROLLBACK
 static __inline__ void ywrap_up(int unit, struct vc_data *conp,
 				struct display *p, int count)
-#else
-static __inline__ void ywrap_up(int unit, struct display *p, int count)
-#endif
 {
     p->yscroll += count;
     if (p->yscroll >= p->vrows)	/* Deal with wrap */
@@ -603,21 +644,15 @@
     p->var.yoffset = p->yscroll*p->fontheight;
     p->var.vmode |= FB_VMODE_YWRAP;
     p->fb_info->updatevar(unit, p->fb_info);
-#if SUPPORT_SCROLLBACK
     scrollback_max += count;
-    if (scrollback_max > p->vrows-conp->vc_rows)
-	scrollback_max = p->vrows-conp->vc_rows;
+    if (scrollback_max > scrollback_phys_max)
+	scrollback_max = scrollback_phys_max;
     scrollback_current = 0;
-#endif
 }
 
 
-#if SUPPORT_SCROLLBACK
 static __inline__ void ywrap_down(int unit, struct vc_data *conp,
 				  struct display *p, int count)
-#else
-static __inline__ void ywrap_down(int unit, struct display *p, int count)
-#endif
 {
     p->yscroll -= count;
     if (p->yscroll < 0)		/* Deal with wrap */
@@ -626,12 +661,10 @@
     p->var.yoffset = p->yscroll*p->fontheight;
     p->var.vmode |= FB_VMODE_YWRAP;
     p->fb_info->updatevar(unit, p->fb_info);
-#if SUPPORT_SCROLLBACK
     scrollback_max -= count;
     if (scrollback_max < 0)
 	scrollback_max = 0;
     scrollback_current = 0;
-#endif
 }
 
 
@@ -639,15 +672,19 @@
 			       struct display *p, int count)
 {
     p->yscroll += count;
-    if (p->yscroll+conp->vc_rows > p->vrows) {
-	p->dispsw->bmove(p, p->yscroll, 0, 0, 0, conp->vc_rows-count,
-			 conp->vc_cols);
-	p->yscroll = 0;
+    if (p->yscroll > p->vrows-conp->vc_rows) {
+	p->dispsw->bmove(p, p->vrows-conp->vc_rows, 0, 0, 0,
+			 conp->vc_rows, conp->vc_cols);
+	p->yscroll -= p->vrows-conp->vc_rows;
     }
     p->var.xoffset = 0;
     p->var.yoffset = p->yscroll*p->fontheight;
     p->var.vmode &= ~FB_VMODE_YWRAP;
     p->fb_info->updatevar(unit, p->fb_info);
+    scrollback_max += count;
+    if (scrollback_max > scrollback_phys_max)
+	scrollback_max = scrollback_phys_max;
+    scrollback_current = 0;
 }
 
 
@@ -656,34 +693,92 @@
 {
     p->yscroll -= count;
     if (p->yscroll < 0) {
-	p->dispsw->bmove(p, p->yscroll + count, 0,
-			 p->vrows - conp->vc_rows + count, 0,
-			 conp->vc_rows - count, conp->vc_cols);
-	p->yscroll = p->vrows - conp->vc_rows;
+	p->dispsw->bmove(p, 0, 0, p->vrows-conp->vc_rows, 0,
+			 conp->vc_rows, conp->vc_cols);
+	p->yscroll += p->vrows-conp->vc_rows;
     }
     p->var.xoffset = 0;
     p->var.yoffset = p->yscroll*p->fontheight;
     p->var.vmode &= ~FB_VMODE_YWRAP;
     p->fb_info->updatevar(unit, p->fb_info);
+    scrollback_max -= count;
+    if (scrollback_max < 0)
+	scrollback_max = 0;
+    scrollback_current = 0;
+}
+
+
+static void fbcon_redraw(struct vc_data *conp, struct display *p, 
+			 int line, int count, int offset)
+{
+    unsigned short *d = (unsigned short *)
+	(conp->vc_origin + conp->vc_size_row * line);
+    unsigned short *s = d + offset;
+    while (count--) {
+	unsigned short *start = s;
+	unsigned short *le = (unsigned short *)
+	    ((unsigned long)s + conp->vc_size_row);
+	unsigned short c;
+	int x = 0;
+	unsigned short attr = 1;
+
+	do {
+	    c = scr_readw(s);
+	    if (attr != (c & 0xff00)) {
+		attr = c & 0xff00;
+		if (s > start) {
+		    p->dispsw->putcs(conp, p, start, s - start, line, x);
+		    x += s - start;
+		    start = s;
+		}
+	    }
+	    if (c == scr_readw(d)) {
+	    	if (s > start) {
+	    	    p->dispsw->putcs(conp, p, start, s - start, line, x);
+		    x += s - start + 1;
+		    start = s + 1;
+	    	} else {
+	    	    x++;
+	    	    start++;
+	    	}
+	    }
+	    scr_writew(c, d);
+	    s++;
+	    d++;
+	} while (s < le);
+	if (s > start)
+	    p->dispsw->putcs(conp, p, start, s - start, line, x);
+	if (offset > 0)
+		line++;
+	else {
+		line--;
+		/* NOTE: We subtract two lines from these pointers */
+		s -= conp->vc_size_row;
+		d -= conp->vc_size_row;
+	}
+    }
 }
 
 
-static void fbcon_scroll(struct vc_data *conp, int t, int b, int dir,
-			 int count)
+static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir,
+			int count)
 {
     int unit = conp->vc_num;
     struct display *p = &fb_display[unit];
+    int is_txt = (p->type == FB_TYPE_TEXT);
 
     if (!p->can_soft_blank && console_blanked)
-	return;
+	return 0;
 
     if (!count)
-	return;
+	return 0;
 
     fbcon_cursor(conp, CM_ERASE);
 
     /*
      * ++Geert: Only use ywrap/ypan if the console is in text mode
+     * ++Andrew: Only use ypan on hardware text mode when scrolling the
+     *           whole screen (prevents flicker).
      */
 
     switch (dir) {
@@ -697,11 +792,7 @@
 			    if (t > 0)
 				fbcon_bmove(conp, 0, 0, count, 0, t,
 					    conp->vc_cols);
-#if SUPPORT_SCROLLBACK
 			    ywrap_up(unit, conp, p, count);
-#else
-			    ywrap_up(unit, p, count);
-#endif
 			    if (conp->vc_rows-b > 0)
 				fbcon_bmove(conp, b-count, 0, b, 0,
 					    conp->vc_rows-b, conp->vc_cols);
@@ -712,7 +803,8 @@
 			break;
 
 		    case SCROLL_YPAN:
-			if (b-t-count > 3*conp->vc_rows>>2) {
+			if (( is_txt && (b-t == conp->vc_rows)) ||
+			    (!is_txt && (b-t-count > 3*conp->vc_rows>>2))) {
 			    if (t > 0)
 				fbcon_bmove(conp, 0, 0, count, 0, t,
 					    conp->vc_cols);
@@ -732,6 +824,15 @@
 			p->dispsw->clear(conp, p, b-count, 0, count,
 					 conp->vc_cols);
 			break;
+		    case SCROLL_YREDRAW:
+		    	fbcon_redraw(conp, p, t, b-t-count, count*conp->vc_cols);
+		    	p->dispsw->clear(conp, p, b-count, 0, count,
+		    			 conp->vc_cols);
+		    	scr_memsetw((unsigned short *)(conp->vc_origin + 
+		    		    conp->vc_size_row * (b-count)), 
+		    		    conp->vc_video_erase_char,
+		    		    conp->vc_size_row * count);
+		    	return 1;
 		}
 	    else {
 		fbcon_bmove(conp, t+count, 0, t, 0, b-t-count, conp->vc_cols);
@@ -749,11 +850,7 @@
 			    if (conp->vc_rows-b > 0)
 				fbcon_bmove(conp, b, 0, b-count, 0,
 					    conp->vc_rows-b, conp->vc_cols);
-#if SUPPORT_SCROLLBACK
 			    ywrap_down(unit, conp, p, count);
-#else
-			    ywrap_down(unit, p, count);
-#endif
 			    if (t > 0)
 				fbcon_bmove(conp, count, 0, 0, 0, t,
 					    conp->vc_cols);
@@ -764,7 +861,8 @@
 			break;
 
 		    case SCROLL_YPAN:
-			if (b-t-count > 3*conp->vc_rows>>2) {
+			if (( is_txt && (b-t == conp->vc_rows)) ||
+			    (!is_txt && (b-t-count > 3*conp->vc_rows>>2))) {
 			    if (conp->vc_rows-b > 0)
 				fbcon_bmove(conp, b, 0, b-count, 0,
 					    conp->vc_rows-b, conp->vc_cols);
@@ -783,6 +881,15 @@
 					 conp->vc_cols);
 			p->dispsw->clear(conp, p, t, 0, count, conp->vc_cols);
 			break;
+			
+		    case SCROLL_YREDRAW:
+			fbcon_redraw(conp, p, b - 1, b-t-count, -count*conp->vc_cols);
+			p->dispsw->clear(conp, p, t, 0, count, conp->vc_cols);
+		    	scr_memsetw((unsigned short *)(conp->vc_origin + 
+		    		    conp->vc_size_row * t), 
+		    		    conp->vc_video_erase_char,
+		    		    conp->vc_size_row * count);
+		    	return 1;
 		}
 	    else {
 		/*
@@ -806,6 +913,7 @@
 	    fbcon_clear(conp, 0, t, conp->vc_rows, count);
 	    break;
     }
+    return 0;
 }
 
 
@@ -876,13 +984,26 @@
     struct display *p = &fb_display[unit];
     struct fb_info *info = p->fb_info;
 
-    if (info && info->switch_con)
-	(*info->switch_con)(conp->vc_num, info);
-#if SUPPORT_SCROLLBACK
+    p->var.yoffset = p->yscroll*p->fontheight;
+    switch (p->scrollmode) {
+	case SCROLL_YWRAP:
+	    scrollback_phys_max = p->vrows-conp->vc_rows;
+	    break;
+	case SCROLL_YPAN:
+	    scrollback_phys_max = p->vrows-2*conp->vc_rows;
+	    if (scrollback_phys_max < 0)
+		scrollback_phys_max = 0;
+	    break;
+	default:
+	    scrollback_phys_max = 0;
+	    break;
+    }
     scrollback_max = 0;
     scrollback_current = 0;
-#endif
-    return(0);
+
+    if (info && info->switch_con)
+	(*info->switch_con)(conp->vc_num, info);
+    return 1;
 }
 
 
@@ -896,28 +1017,28 @@
     if (!p->can_soft_blank) {
 	if (blank) {
 #ifdef CONFIG_MAC
-	    if (MACH_IS_MAC)
-		mymemset(p->screen_base,
-			 p->var.xres_virtual*p->var.yres_virtual*
-			 p->var.bits_per_pixel>>3);
-	    else
-#endif
-	    if (p->visual == FB_VISUAL_MONO01)
-		mymemset(p->screen_base,
-			 p->var.xres_virtual*p->var.yres_virtual*
-			 p->var.bits_per_pixel>>3);
-	     else
-		 mymemclear(p->screen_base,
-			    p->var.xres_virtual*p->var.yres_virtual*
-			    p->var.bits_per_pixel>>3);
-	    return(0);
+	    if (MACH_IS_MAC) {
+		if (p->screen_base)
+		    mymemset(p->screen_base,
+			     p->var.xres_virtual*p->var.yres_virtual*
+			     p->var.bits_per_pixel>>3);
+	    } else
+#endif
+	    if (p->visual == FB_VISUAL_MONO01) {
+		if (p->screen_base)
+		    mymemset(p->screen_base,
+			     p->var.xres_virtual*p->var.yres_virtual*
+			     p->var.bits_per_pixel>>3);
+	     } else
+		 p->dispsw->clear(p->conp, p, 0, 0, p->conp->vc_rows, p->conp->vc_cols);
+	    return 0;
 	} else {
 	    /* Tell console.c that it has to restore the screen itself */
-	    return(1);
+	    return 1;
 	}
     }
     (*info->blank)(blank, info);
-    return(0);
+    return 0;
 }
 
 
@@ -934,12 +1055,12 @@
 
     if (alloc < size)
 	/* allocation length not sufficient */
-	return( -ENAMETOOLONG );
+	return -ENAMETOOLONG;
 
     for (i = 0; i < 256; i++)
 	for (j = 0; j < p->fontheight; j++)
 	    data[i*32+j] = p->fontdata[i*p->fontheight+j];
-    return( 0 );
+    return 0;
 }
 
 
@@ -967,19 +1088,19 @@
 	name[sizeof(name)-1] = 0;
 
 	if (!findsoftfont( name, &w, &h, (u8 **)&data ))
-	    return( -ENOENT );
+	    return -ENOENT;
 	userspace = 0;
     } else if (w == 1) {
 	/* copy font from some other console in 'h'*/
 	struct display *op;
 
 	if (h < 0 || !vc_cons_allocated( h ))
-	    return( -ENOTTY );
+	    return -ENOTTY;
 	if (h == unit)
-	    return( 0 ); /* nothing to do */
+	    return 0; /* nothing to do */
 	op = &fb_display[h];
 	if (op->fontdata == p->fontdata)
-	    return( 0 ); /* already the same font... */
+	    return 0; /* already the same font... */
 
 	resize = (op->fontwidth != p->fontwidth) ||
 		 (op->fontheight != p->fontheight);
@@ -995,7 +1116,7 @@
 
     if (w != 8)
 	/* Currently only fontwidth == 8 supported */
-	return( -ENXIO );
+	return -ENXIO;
 
     resize = (w != p->fontwidth) || (h != p->fontheight);
     size = (w+7)/8 * h * 256;
@@ -1005,7 +1126,7 @@
 
     if (userspace) {
 	if (!(new_data = kmalloc( sizeof(int)+size, GFP_USER )))
-	    return( -ENOMEM );
+	    return -ENOMEM;
 	new_data += sizeof(int);
 	REFCOUNT(new_data) = 1; /* usage counter */
 
@@ -1037,7 +1158,7 @@
     if (old_data && (--REFCOUNT(old_data) == 0))
 	kfree( old_data - sizeof(int) );
 
-    return( 0 );
+    return 0;
 }
 
 static u16 palette_red[16];
@@ -1056,7 +1177,7 @@
     u8 val;
 
     if (!conp->vc_can_do_color || (!p->can_soft_blank && console_blanked))
-	return(-EINVAL);
+	return -EINVAL;
     for (i = j = 0; i < 16; i++) {
 	k = table[i];
 	val = conp->vc_palette[j++];
@@ -1075,43 +1196,52 @@
 
 static int fbcon_scrolldelta(struct vc_data *conp, int lines)
 {
-#if SUPPORT_SCROLLBACK
-    int unit = fg_console; /* xxx */
-    struct display *p = &fb_display[unit];
-    int offset;
+    int unit, offset, limit, scrollback_old;
+    struct display *p;
 
-    if (!p->can_soft_blank && console_blanked ||
-	vt_cons[unit]->vc_mode != KD_TEXT || !lines ||
-	p->scrollmode != SCROLL_YWRAP)
-	return 0;
+    /* FIXME: Sync to new code, remember to set visible_origin */
 
-    fbcon_cursor(conp, CM_ERASE);
+    if (!scrollback_phys_max)
+	return -ENOSYS;
 
+    scrollback_old = scrollback_current;
     scrollback_current -= lines;
     if (scrollback_current < 0)
 	scrollback_current = 0;
     else if (scrollback_current > scrollback_max)
 	scrollback_current = scrollback_max;
+    if (scrollback_current == scrollback_old)
+	return 0;
+
+    unit = fg_console;
+    p = &fb_display[unit];
+    if (!p->can_soft_blank &&
+	(console_blanked || vt_cons[unit]->vc_mode != KD_TEXT || !lines))
+	return 0;
+    fbcon_cursor(conp, CM_ERASE);
 
     offset = p->yscroll-scrollback_current;
+    limit = p->vrows;
+    switch (p->scrollmode) {
+	case SCROLL_YWRAP:
+	    p->var.vmode |= FB_VMODE_YWRAP;
+	    break;
+	case SCROLL_YPAN:
+	    limit -= conp->vc_rows;
+	    p->var.vmode &= ~FB_VMODE_YWRAP;
+	    break;
+    }
     if (offset < 0)
-	offset += p->vrows;
-    else if (offset > p->vrows)
-	offset -= p->vrows;
-    p->var.vmode |= FB_VMODE_YWRAP;
+	offset += limit;
+    else if (offset >= limit)
+	offset -= limit;
     p->var.xoffset = 0;
     p->var.yoffset = offset*p->fontheight;
     p->fb_info->updatevar(unit, p->fb_info);
-#else
-    return -ENOSYS;
-#endif
+    return 0;
 }
 
 
-#define LOGO_H			80
-#define LOGO_W			80
-#define LOGO_LINE	(LOGO_W/8)
-
 __initfunc(static int fbcon_show_logo( void ))
 {
     struct display *p = &fb_display[fg_console]; /* draw to vt in foreground */
@@ -1120,9 +1250,13 @@
     unsigned char *fb = p->screen_base;
     unsigned char *logo;
     unsigned char *dst, *src;
-    int i, j, n, x1, y1;
+    int i, j, n, x1, y1, x;
     int logo_depth, done = 0;
 
+    /* Return if the frame buffer is not mapped */
+    if (!fb)
+	return 0;
+
     /* Set colors if visual is PSEUDOCOLOR and we have enough colors, or for
      * TRUECOLOR */
     if ((p->visual == FB_VISUAL_PSEUDOCOLOR && depth >= 4) ||
@@ -1182,216 +1316,214 @@
 	logo_depth = 1;
     }
 
+    for (x = 0; x < smp_num_cpus * (LOGO_W + 8) &&
+    	 x < p->var.xres - (LOGO_W + 8); x += (LOGO_W + 8)) {
+    	 
 #if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CFB24) || \
-    defined(CONFIG_FBCON_CFB32)
-    if (p->visual == FB_VISUAL_TRUECOLOR) {
-	unsigned int val;		/* max. depth 32! */
-	int bdepth;
-	int redshift, greenshift, blueshift;
+    defined(CONFIG_FBCON_CFB32) || defined(CONFIG_FB_SBUS)
+        if (p->visual == FB_VISUAL_TRUECOLOR) {
+	    unsigned int val;		/* max. depth 32! */
+	    int bdepth;
+	    int redshift, greenshift, blueshift;
 		
-	/* Bug: Doesn't obey msb_right ... (who needs that?) */
-	redshift   = p->var.red.offset;
-	greenshift = p->var.green.offset;
-	blueshift  = p->var.blue.offset;
-
-	if (depth >= 24 && (depth % 8) == 0) {
-	    /* have at least 8 bits per color */
-	    src = logo;
-	    bdepth = depth/8;
-	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
-		dst = fb + y1*line;
-		for( x1 = 0; x1 < LOGO_W; x1++, src++ ) {
-		    val = (*src << redshift) |
-			  (*src << greenshift) |
-			  (*src << blueshift);
+	    /* Bug: Doesn't obey msb_right ... (who needs that?) */
+	    redshift   = p->var.red.offset;
+	    greenshift = p->var.green.offset;
+	    blueshift  = p->var.blue.offset;
+
+	    if (depth >= 24 && (depth % 8) == 0) {
+		/* have at least 8 bits per color */
+		src = logo;
+		bdepth = depth/8;
+		for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		    dst = fb + y1*line + x*bdepth;
+		    for( x1 = 0; x1 < LOGO_W; x1++, src++ ) {
+			val = (*src << redshift) |
+			      (*src << greenshift) |
+			      (*src << blueshift);
 #ifdef __LITTLE_ENDIAN
-		    for( i = 0; i < bdepth; ++i )
+			for( i = 0; i < bdepth; ++i )
 #else
-		    for( i = bdepth-1; i >= 0; --i )
+			for( i = bdepth-1; i >= 0; --i )
 #endif
-			*dst++ = val >> (i*8);
+			    *dst++ = val >> (i*8);
+		    }
 		}
 	    }
-	}
-	else if (depth >= 15 && depth <= 23) {
-	    /* have 5..7 bits per color, using 16 color image */
-	    unsigned int pix;
-	    src = linux_logo16;
-	    bdepth = (depth+7)/8;
-	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
-		dst = fb + y1*line;
-		for( x1 = 0; x1 < LOGO_W/2; x1++, src++ ) {
-		    pix = (*src >> 4) | 0x10; /* upper nibble */
-		    val = (pix << redshift) |
-			  (pix << greenshift) |
-			  (pix << blueshift);
-		    for( i = 0; i < bdepth; ++i )
-			*dst++ = val >> (i*8);
-		    pix = (*src & 0x0f) | 0x10; /* lower nibble */
-		    val = (pix << redshift) |
-			  (pix << greenshift) |
-			  (pix << blueshift);
-		    for( i = bdepth-1; i >= 0; --i )
-			*dst++ = val >> (i*8);
+	    else if (depth >= 15 && depth <= 23) {
+	        /* have 5..7 bits per color, using 16 color image */
+		unsigned int pix;
+		src = linux_logo16;
+		bdepth = (depth+7)/8;
+		for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		    dst = fb + y1*line + x*bdepth;
+		    for( x1 = 0; x1 < LOGO_W/2; x1++, src++ ) {
+			pix = (*src >> 4) | 0x10; /* upper nibble */
+			val = (pix << redshift) |
+			      (pix << greenshift) |
+			      (pix << blueshift);
+			for( i = 0; i < bdepth; ++i )
+			    *dst++ = val >> (i*8);
+			pix = (*src & 0x0f) | 0x10; /* lower nibble */
+			val = (pix << redshift) |
+			      (pix << greenshift) |
+			      (pix << blueshift);
+			for( i = bdepth-1; i >= 0; --i )
+			    *dst++ = val >> (i*8);
+		    }
 		}
 	    }
-	}
-	
-	done = 1;
-    }
+	    done = 1;
+        }
 #endif
 #if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CFB24) || \
-    defined(CONFIG_FBCON_CFB32)
-    if ((depth % 8 == 0) && (p->visual == FB_VISUAL_DIRECTCOLOR)) {
-	/* Modes without color mapping, needs special data transformation... */
-	unsigned int val;		/* max. depth 32! */
-	int bdepth = depth/8;
-	unsigned char mask[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
-	unsigned char redmask, greenmask, bluemask;
-	int redshift, greenshift, blueshift;
+    defined(CONFIG_FBCON_CFB32) || defined(CONFIG_FB_SBUS)
+	if ((depth % 8 == 0) && (p->visual == FB_VISUAL_DIRECTCOLOR)) {
+	    /* Modes without color mapping, needs special data transformation... */
+	    unsigned int val;		/* max. depth 32! */
+	    int bdepth = depth/8;
+	    unsigned char mask[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
+	    unsigned char redmask, greenmask, bluemask;
+	    int redshift, greenshift, blueshift;
 		
-	/* Bug: Doesn't obey msb_right ... (who needs that?) */
-	redmask   = mask[p->var.red.length   < 8 ? p->var.red.length   : 8];
-	greenmask = mask[p->var.green.length < 8 ? p->var.green.length : 8];
-	bluemask  = mask[p->var.blue.length  < 8 ? p->var.blue.length  : 8];
-	redshift   = p->var.red.offset   - (8-p->var.red.length);
-	greenshift = p->var.green.offset - (8-p->var.green.length);
-	blueshift  = p->var.blue.offset  - (8-p->var.blue.length);
-
-	src = logo;
-	for( y1 = 0; y1 < LOGO_H; y1++ ) {
-	    dst = fb + y1*line;
-	    for( x1 = 0; x1 < LOGO_W; x1++, src++ ) {
-		val = ((linux_logo_red[*src-32]   & redmask)   << redshift) |
-		      ((linux_logo_green[*src-32] & greenmask) << greenshift) |
-		      ((linux_logo_blue[*src-32]  & bluemask)  << blueshift);
+	    /* Bug: Doesn't obey msb_right ... (who needs that?) */
+	    redmask   = mask[p->var.red.length   < 8 ? p->var.red.length   : 8];
+	    greenmask = mask[p->var.green.length < 8 ? p->var.green.length : 8];
+	    bluemask  = mask[p->var.blue.length  < 8 ? p->var.blue.length  : 8];
+	    redshift   = p->var.red.offset   - (8-p->var.red.length);
+	    greenshift = p->var.green.offset - (8-p->var.green.length);
+	    blueshift  = p->var.blue.offset  - (8-p->var.blue.length);
+
+	    src = logo;
+	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		dst = fb + y1*line + x*bdepth;
+		for( x1 = 0; x1 < LOGO_W; x1++, src++ ) {
+		    val = ((linux_logo_red[*src-32]   & redmask)   << redshift) |
+		          ((linux_logo_green[*src-32] & greenmask) << greenshift) |
+		          ((linux_logo_blue[*src-32]  & bluemask)  << blueshift);
 #ifdef __LITTLE_ENDIAN
-		for( i = 0; i < bdepth; ++i )
+		    for( i = 0; i < bdepth; ++i )
 #else
-		for( i = bdepth-1; i >= 0; --i )
+		    for( i = bdepth-1; i >= 0; --i )
 #endif
-		    *dst++ = val >> (i*8);
+			*dst++ = val >> (i*8);
+		}
 	    }
+	    done = 1;
 	}
-		
-	done = 1;
-    }
 #endif
-#if defined(CONFIG_FBCON_CFB8)
-    if (depth == 8 && p->type == FB_TYPE_PACKED_PIXELS) {
-	/* depth 8 or more, packed, with color registers */
+#if defined(CONFIG_FBCON_CFB8) || defined(CONFIG_FB_SBUS)
+	if (depth == 8 && p->type == FB_TYPE_PACKED_PIXELS) {
+	    /* depth 8 or more, packed, with color registers */
 		
-	src = logo;
-	for( y1 = 0; y1 < LOGO_H; y1++ ) {
-	    dst = fb + y1*line;
-	    for( x1 = 0; x1 < LOGO_W; x1++ ) {
-		*dst++ = *src++;
+	    src = logo;
+	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		dst = fb + y1*line + x;
+		for( x1 = 0; x1 < LOGO_W; x1++ )
+		    *dst++ = *src++;
 	    }
+	    done = 1;
 	}
-
-	done = 1;
-    }
 #endif
 #if defined(CONFIG_FBCON_AFB) || defined(CONFIG_FBCON_ILBM) || \
     defined(CONFIG_FBCON_IPLAN2P2) || defined(CONFIG_FBCON_IPLAN2P4) || \
     defined(CONFIG_FBCON_IPLAN2P8)
-    if (depth >= 2 && (p->type == FB_TYPE_PLANES ||
-		       p->type == FB_TYPE_INTERLEAVED_PLANES)) {
-	/* planes (normal or interleaved), with color registers */
-	int bit;
-	unsigned char val, mask;
-	int plane = p->next_plane;
+	if (depth >= 2 && (p->type == FB_TYPE_PLANES ||
+			   p->type == FB_TYPE_INTERLEAVED_PLANES)) {
+	    /* planes (normal or interleaved), with color registers */
+	    int bit;
+	    unsigned char val, mask;
+	    int plane = p->next_plane;
 
-	/* for support of Atari interleaved planes */
+	    /* for support of Atari interleaved planes */
 #define MAP_X(x)	(plane > line ? x : (x & ~1)*depth + (x & 1))
-	/* extract a bit from the source image */
+	    /* extract a bit from the source image */
 #define	BIT(p,pix,bit)	(p[pix*logo_depth/8] & \
 			 (1 << ((8-((pix*logo_depth)&7)-logo_depth) + bit)))
 		
-	src = logo;
-	for( y1 = 0; y1 < LOGO_H; y1++ ) {
-	    for( x1 = 0; x1 < LOGO_LINE; x1++, src += logo_depth ) {
-		dst = fb + y1*line + MAP_X(x1);
-		for( bit = 0; bit < logo_depth; bit++ ) {
-		    val = 0;
-		    for( mask = 0x80, i = 0; i < 8; mask >>= 1, i++ ) {
-			if (BIT( src, i, bit ))
-			    val |= mask;
+	    src = logo;
+	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		for( x1 = 0; x1 < LOGO_LINE; x1++, src += logo_depth ) {
+		    dst = fb + y1*line + MAP_X(x1);
+		    for( bit = 0; bit < logo_depth; bit++ ) {
+			val = 0;
+			for( mask = 0x80, i = 0; i < 8; mask >>= 1, i++ ) {
+			    if (BIT( src, i, bit ))
+				val |= mask;
+			}
+			*dst = val;
+			dst += plane;
 		    }
-		    *dst = val;
-		    dst += plane;
 		}
 	    }
-	}
 	
-	/* fill remaining planes
-	 * special case for logo_depth == 4: we used color registers 16..31,
-	 * so fill plane 4 with 1 bits instead of 0 */
-	if (depth > logo_depth) {
-	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
-		for( x1 = 0; x1 < LOGO_LINE; x1++ ) {
-		    dst = fb + y1*line + MAP_X(x1) + logo_depth*plane;
-		    for( i = logo_depth; i < depth; i++, dst += plane )
-			*dst = (i == logo_depth && logo_depth == 4)
-			       ? 0xff : 0x00;
+	    /* fill remaining planes
+	     * special case for logo_depth == 4: we used color registers 16..31,
+	     * so fill plane 4 with 1 bits instead of 0 */
+	    if (depth > logo_depth) {
+		for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		    for( x1 = 0; x1 < LOGO_LINE; x1++ ) {
+			dst = fb + y1*line + MAP_X(x1) + logo_depth*plane;
+			for( i = logo_depth; i < depth; i++, dst += plane )
+			    *dst = (i == logo_depth && logo_depth == 4)
+				   ? 0xff : 0x00;
+		    }
 		}
 	    }
+	    done = 1;
+	    break;
 	}
-	
-	done = 1;
-    }
 #endif
 #if defined(CONFIG_FBCON_MFB) || defined(CONFIG_FBCON_AFB) || \
     defined(CONFIG_FBCON_ILBM)
-    if (depth == 1 && (p->type == FB_TYPE_PACKED_PIXELS ||
-		       p->type == FB_TYPE_PLANES ||
-		       p->type == FB_TYPE_INTERLEAVED_PLANES)) {
-	/* monochrome */
-	unsigned char inverse = p->inverse ? 0x00 : 0xff;
-
-	/* can't use simply memcpy because need to apply inverse */
-	for( y1 = 0; y1 < LOGO_H; y1++ ) {
-	    src = logo + y1*LOGO_LINE;
-	    dst = fb + y1*line;
-	    for( x1 = 0; x1 < LOGO_LINE; ++x1 )
-		*dst++ = *src++ ^ inverse;
-	}
-
-	done = 1;
-    }
-#endif
-#ifdef CONFIG_FBCON_VGA
-    if (depth == 1 && p->type == FB_TYPE_VGA_TEXT) {
 
-        int height = 0;
-        char *p;
-    
-        printk(linux_mda_image);
+	if (depth == 1 && (p->type == FB_TYPE_PACKED_PIXELS ||
+			   p->type == FB_TYPE_PLANES ||
+			   p->type == FB_TYPE_INTERLEAVED_PLANES)) {
 
-        for (p = linux_mda_image; *p; p++)
-            if (*p == '\n')
-                height++;
+	    /* monochrome */
+	    unsigned char inverse = p->inverse ? 0x00 : 0xff;
 
-        return height;
-    }
+	    /* can't use simply memcpy because need to apply inverse */
+	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		src = logo + y1*LOGO_LINE + x/8;
+		dst = fb + y1*line;
+		for( x1 = 0; x1 < LOGO_LINE; ++x1 )
+		    *dst++ = *src++ ^ inverse;
+	    }
+	    done = 1;
+	}
 #endif
+    }
+    
     /* Modes not yet supported: packed pixels with depth != 8 (does such a
      * thing exist in reality?) */
 
-    return( done ? LOGO_H/p->fontheight + 1 : 0 );
+    return done ? (LOGO_H + p->fontheight - 1) / p->fontheight : 0 ;
 }
 
-
-
 /*
  *  The console `switch' structure for the frame buffer based console
  */
-
+ 
 struct consw fb_con = {
-    fbcon_startup, fbcon_init, fbcon_deinit, fbcon_clear, fbcon_putc,
-    fbcon_putcs, fbcon_cursor, fbcon_scroll, fbcon_bmove, fbcon_switch,
-    fbcon_blank, fbcon_get_font, fbcon_set_font, fbcon_set_palette,
-    fbcon_scrolldelta
+    con_startup: 	fbcon_startup, 
+    con_init: 		fbcon_init,
+    con_deinit: 	fbcon_deinit,
+    con_clear: 		fbcon_clear,
+    con_putc: 		fbcon_putc,
+    con_putcs: 		fbcon_putcs,
+    con_cursor: 	fbcon_cursor,
+    con_scroll: 	fbcon_scroll,
+    con_bmove: 		fbcon_bmove,
+    con_switch: 	fbcon_switch,
+    con_blank: 		fbcon_blank,
+    con_get_font: 	fbcon_get_font,
+    con_set_font: 	fbcon_set_font,
+    con_set_palette: 	fbcon_set_palette,
+    con_scrolldelta: 	fbcon_scrolldelta,
+    con_set_origin: 	NULL,
+    con_save_screen: 	NULL,
 };
 
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov