patch-2.1.67 linux/drivers/video/fbcon.c
Next file: linux/drivers/video/fbcon.h
Previous file: linux/drivers/video/fbcon-retz3.c
Back to the patch index
Back to the overall index
- Lines: 1476
- Date:
Mon Nov 24 01:18:56 1997
- Orig file:
v2.1.66/linux/drivers/video/fbcon.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.66/linux/drivers/video/fbcon.c linux/drivers/video/fbcon.c
@@ -0,0 +1,1475 @@
+/*
+ * linux/drivers/video/fbcon.c -- Low level frame buffer based console driver
+ *
+ * Copyright (C) 1995 Geert Uytterhoeven
+ *
+ *
+ * This file is based on the original Amiga console driver (amicon.c):
+ *
+ * Copyright (C) 1993 Hamish Macdonald
+ * Greg Harp
+ * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk]
+ *
+ * with work by William Rucklidge (wjr@cs.cornell.edu)
+ * Geert Uytterhoeven
+ * Jes Sorensen (jds@kom.auc.dk)
+ * Martin Apel
+ *
+ * and on the original Atari console driver (atacon.c):
+ *
+ * Copyright (C) 1993 Bjoern Brauel
+ * Roman Hodek
+ *
+ * with work by Guenther Kelleter
+ * Martin Schaller
+ * Andreas Schwab
+ *
+ *
+ * The low level operations for the various display memory organizations are
+ * now in separate source files.
+ *
+ * Currently only the following organizations are supported:
+ *
+ * - non-accelerated:
+ *
+ * o mfb Monochrome
+ * o ilbm Interleaved bitplanes à la Amiga
+ * o afb Bitplanes à la Amiga
+ * o iplan2p[248] Interleaved bitplanes à la Atari (2, 4 and 8 planes)
+ * o cfb{8,16} Packed pixels (8 and 16 bpp)
+ *
+ * - accelerated:
+ *
+ * o cyber CyberVision64 packed pixels (accelerated)
+ * o retz3 Retina Z3 packed pixels (accelerated)
+ * o mach64 ATI Mach 64 packed pixels (accelerated)
+ *
+ * To do:
+ *
+ * - Implement 16 plane mode (iplan2p16)
+ * - Add support for 24/32 bit packed pixels (cfb{24,32})
+ * - Hardware cursor
+ *
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#define SUPPORT_SCROLLBACK 0
+#define FLASHING_CURSOR 1
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/kd.h>
+#include <linux/malloc.h>
+#include <linux/fb.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+#include <linux/init.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#ifdef CONFIG_AMIGA
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#endif /* CONFIG_AMIGA */
+#ifdef CONFIG_ATARI
+#include <asm/atariints.h>
+#endif
+#ifdef __mc68000__
+#include <asm/machdep.h>
+#include <asm/setup.h>
+#endif
+#include <asm/linux_logo.h>
+
+#include "fbcon.h"
+#include "font.h"
+
+
+struct display fb_display[MAX_NR_CONSOLES];
+
+
+/* ++Geert: Sorry, no hardware cursor support at the moment;
+ use Atari alike software cursor */
+
+#if FLASHING_CURSOR
+static int cursor_drawn = 0;
+
+#define CURSOR_DRAW_DELAY (2)
+
+/* # VBL ints between cursor state changes */
+#define AMIGA_CURSOR_BLINK_RATE (20)
+#define ATARI_CURSOR_BLINK_RATE (42)
+#define DEFAULT_CURSOR_BLINK_RATE (20)
+
+static int vbl_cursor_cnt = 0;
+static int cursor_on = 0;
+static int cursor_blink_rate;
+
+static __inline__ int CURSOR_UNDRAWN(void)
+{
+ int cursor_was_drawn;
+ vbl_cursor_cnt = 0;
+ cursor_was_drawn = cursor_drawn;
+ cursor_drawn = 0;
+ return(cursor_was_drawn);
+}
+#endif
+
+/*
+ * Scroll Method
+ */
+
+#define SCROLL_YWRAP (0)
+#define SCROLL_YPAN (1)
+#define SCROLL_YMOVE (2)
+
+#define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1)
+
+
+/*
+ * 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 int fbcon_deinit(struct vc_data *conp);
+static int fbcon_changevar(int con);
+static int fbcon_clear(struct vc_data *conp, int sy, int sx, int height,
+ int width);
+static int fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos);
+static int fbcon_putcs(struct vc_data *conp, const char *s, int count,
+ int ypos, int xpos);
+static int fbcon_cursor(struct vc_data *conp, int mode);
+static int fbcon_scroll(struct vc_data *conp, int t, int b,
+ int dir, int count);
+static int fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+ int height, int width);
+static int fbcon_switch(struct vc_data *conp);
+static int fbcon_blank(int blank);
+static int fbcon_get_font(struct vc_data *conp, int *w, int *h, char *data);
+static int fbcon_set_font(struct vc_data *conp, int w, int h, char *data);
+static int fbcon_set_palette(struct vc_data *conp, unsigned char *table);
+static int fbcon_scrolldelta(int lines);
+int fbcon_register_driver(struct display_switch *dispsw, int is_accel);
+int fbcon_unregister_driver(struct display_switch *dispsw);
+
+
+/*
+ * Internal routines
+ */
+
+static void fbcon_setup(int con, int setcol, int init);
+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,
+ struct display *p, int count);
+static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width, u_int y_break);
+static struct display_switch *probe_list(struct display_switch *dispsw,
+ struct display *disp);
+
+#ifdef CONFIG_KERNELD
+static void request_driver(struct display *disp, int is_accel);
+#endif
+static struct display_switch *fbcon_get_driver(struct display *disp);
+static int fbcon_show_logo(void);
+
+#if FLASHING_CURSOR
+static void cursor_timer_handler(unsigned long dev_addr);
+
+static struct timer_list cursor_timer = {
+ NULL, NULL, 0, 0L, cursor_timer_handler
+};
+
+static void cursor_timer_handler(unsigned long dev_addr)
+{
+ fbcon_vbl_handler(0, NULL, NULL);
+ cursor_timer.expires = jiffies+2;
+ cursor_timer.data = 0;
+ cursor_timer.next = cursor_timer.next = NULL;
+ add_timer(&cursor_timer);
+}
+#endif
+
+/*
+ * Low Level Operations
+ */
+
+static struct display_switch dispsw_dummy;
+
+#ifdef CONFIG_FBCON_MFB
+extern int fbcon_init_mfb(void);
+#endif
+#ifdef CONFIG_FBCON_ILBM
+extern int fbcon_init_ilbm(void);
+#endif
+#ifdef CONFIG_FBCON_AFB
+extern int fbcon_init_afb(void);
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P2
+extern int fbcon_init_iplan2p2(void);
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P4
+extern int fbcon_init_iplan2p4(void);
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P8
+extern int fbcon_init_iplan2p8(void);
+#endif
+#ifdef CONFIG_FBCON_CFB8
+extern int fbcon_init_cfb8(void);
+#endif
+#ifdef CONFIG_FBCON_CFB16
+extern int fbcon_init_cfb16(void);
+#endif
+#ifdef CONFIG_FBCON_CFB24
+extern int fbcon_init_cfb24(void);
+#endif
+#ifdef CONFIG_FBCON_CFB32
+extern int fbcon_init_cfb32(void);
+#endif
+#ifdef CONFIG_FBCON_CYBER
+extern int fbcon_init_cyber(void);
+#endif
+#ifdef CONFIG_FBCON_RETINAZ3
+extern int fbcon_init_retz3(void);
+#endif
+#ifdef CONFIG_FBCON_MACH64
+extern int fbcon_init_mach64(void);
+#endif
+
+extern int num_registered_fb;
+
+__initfunc(static unsigned long fbcon_startup(unsigned long kmem_start,
+ const char **display_desc))
+{
+ int irqres = 1;
+
+ /* Probe all frame buffer devices */
+ kmem_start = probe_framebuffers(kmem_start);
+
+ if (!num_registered_fb)
+ return kmem_start;
+
+ /* Initialize all built-in low level drivers */
+#ifdef CONFIG_FBCON_RETINAZ3
+ fbcon_init_retz3();
+#endif
+#ifdef CONFIG_FBCON_MFB
+ fbcon_init_mfb();
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P2
+ fbcon_init_iplan2p2();
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P4
+ fbcon_init_iplan2p4();
+#endif
+#ifdef CONFIG_FBCON_IPLAN2P8
+ fbcon_init_iplan2p8();
+#endif
+#ifdef CONFIG_FBCON_ILBM
+ fbcon_init_ilbm();
+#endif
+#ifdef CONFIG_FBCON_AFB
+ fbcon_init_afb();
+#endif
+#ifdef CONFIG_FBCON_CFB8
+ fbcon_init_cfb8();
+#endif
+#ifdef CONFIG_FBCON_CFB16
+ fbcon_init_cfb16();
+#endif
+#ifdef CONFIG_FBCON_CFB24
+ fbcon_init_cfb24();
+#endif
+#ifdef CONFIG_FBCON_CFB32
+ fbcon_init_cfb32();
+#endif
+#ifdef CONFIG_FBCON_CYBER
+ fbcon_init_cyber();
+#endif
+#ifdef CONFIG_FBCON_MACH64
+ fbcon_init_mach64();
+#endif
+
+ *display_desc = "frame buffer device";
+
+#ifdef CONFIG_AMIGA
+ if (MACH_IS_AMIGA) {
+ cursor_blink_rate = AMIGA_CURSOR_BLINK_RATE;
+ irqres = request_irq(IRQ_AMIGA_VERTB, fbcon_vbl_handler, 0,
+ "console/cursor", fbcon_vbl_handler);
+ }
+#endif /* CONFIG_AMIGA */
+#ifdef CONFIG_ATARI
+ if (MACH_IS_ATARI) {
+ cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
+ irqres = request_irq(IRQ_AUTO_4, fbcon_vbl_handler, IRQ_TYPE_PRIO,
+ "console/cursor", fbcon_vbl_handler);
+ }
+#endif /* CONFIG_ATARI */
+ if (irqres) {
+ cursor_blink_rate = DEFAULT_CURSOR_BLINK_RATE;
+ cursor_timer.expires = jiffies+2;
+ cursor_timer.data = 0;
+ cursor_timer.next = cursor_timer.prev = NULL;
+ add_timer(&cursor_timer);
+ }
+
+ if (!console_show_logo)
+ console_show_logo = fbcon_show_logo;
+
+ return kmem_start;
+}
+
+
+static void fbcon_init(struct vc_data *conp)
+{
+ int unit = conp->vc_num;
+ struct fb_info *info;
+
+ /* on which frame buffer will we open this console? */
+ info = registered_fb[(int)con2fb_map[unit]];
+
+ info->changevar = &fbcon_changevar;
+ fb_display[unit] = *(info->disp); /* copy from default */
+ fb_display[unit].conp = conp;
+ fb_display[unit].fb_info = info;
+ fbcon_setup(unit, 1, 1);
+}
+
+
+static int fbcon_deinit(struct vc_data *conp)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+
+ if (p->dispsw)
+ p->dispsw->release();
+ p->dispsw = 0;
+ p->conp = 0;
+ return(0);
+}
+
+
+static int fbcon_changevar(int con)
+{
+ if (fb_display[con].conp)
+ fbcon_setup(con, 1, 0);
+ return(0);
+}
+
+
+static __inline__ void updatescrollmode(struct display *p)
+{
+ if (divides(p->ywrapstep, p->fontheight) &&
+ divides(p->fontheight, p->var.yres_virtual))
+ p->scrollmode = SCROLL_YWRAP;
+ else if (divides(p->ypanstep, p->fontheight) &&
+ p->var.yres_virtual >= p->var.yres+p->fontheight)
+ p->scrollmode = SCROLL_YPAN;
+ else
+ p->scrollmode = SCROLL_YMOVE;
+}
+
+
+static void fbcon_setup(int con, int setcol, int init)
+{
+ struct display *p = &fb_display[con];
+ struct vc_data *conp = p->conp;
+ int nr_rows, nr_cols;
+ struct display_switch *old_dispsw, *new_dispsw;
+
+ p->var.xoffset = p->var.yoffset = p->yscroll = 0; /* reset wrap/pan */
+
+ if (!p->fb_info->fontname[0] ||
+ !findsoftfont(p->fb_info->fontname, &p->fontwidth, &p->fontheight,
+ &p->fontdata) || p->fontwidth != 8)
+ getdefaultfont(p->var.xres, p->var.yres, NULL, &p->fontwidth,
+ &p->fontheight, &p->fontdata);
+ if (p->fontwidth != 8) {
+ /* ++Geert: changed from panic() to `correct and continue' */
+ printk(KERN_ERR "fbcon_setup: No support for fontwidth != 8");
+ p->fontwidth = 8;
+ }
+ updatescrollmode(p);
+
+ nr_cols = p->var.xres/p->fontwidth;
+ nr_rows = p->var.yres/p->fontheight;
+ /*
+ * ++guenther: console.c:vc_allocate() relies on initializing
+ * vc_{cols,rows}, but we must not set those if we are only
+ * resizing the console.
+ */
+ if (init) {
+ conp->vc_cols = nr_cols;
+ conp->vc_rows = nr_rows;
+ }
+ p->vrows = p->var.yres_virtual/p->fontheight;
+ conp->vc_can_do_color = p->var.bits_per_pixel != 1;
+
+ new_dispsw = fbcon_get_driver(p);
+ if (!new_dispsw) {
+ printk(KERN_WARNING "fbcon_setup: type %d (aux %d, depth %d) not "
+ "supported\n", p->type, p->type_aux, p->var.bits_per_pixel);
+ dispsw_dummy.open(p);
+ new_dispsw = &dispsw_dummy;
+ }
+ /* Be careful when changing dispsw, it might be the current console. */
+ old_dispsw = p->dispsw;
+ p->dispsw = new_dispsw;
+ if (old_dispsw)
+ old_dispsw->release();
+
+ if (setcol) {
+ p->fgcol = p->var.bits_per_pixel > 2 ? 7 : (1<<p->var.bits_per_pixel)-1;
+ p->bgcol = 0;
+ }
+
+ if (!init)
+ vc_resize_con(nr_rows, nr_cols, con);
+}
+
+
+/* ====================================================================== */
+
+/* fbcon_XXX routines - interface used by the world
+ *
+ * This system is now divided into two levels because of complications
+ * caused by hardware scrolling. Top level functions:
+ *
+ * fbcon_bmove(), fbcon_clear(), fbcon_putc()
+ *
+ * handles y values in range [0, scr_height-1] that correspond to real
+ * screen positions. y_wrap shift means that first line of bitmap may be
+ * anywhere on this display. These functions convert lineoffsets to
+ * bitmap offsets and deal with the wrap-around case by splitting blits.
+ *
+ * fbcon_bmove_physical_8() -- These functions fast implementations
+ * fbcon_clear_physical_8() -- of original fbcon_XXX fns.
+ * fbcon_putc_physical_8() -- (fontwidth != 8) may be added later
+ *
+ * WARNING:
+ *
+ * At the moment fbcon_putc() cannot blit across vertical wrap boundary
+ * Implies should only really hardware scroll in rows. Only reason for
+ * restriction is simplicity & efficiency at the moment.
+ */
+
+static __inline__ int real_y(struct display *p, int ypos)
+{
+ int rows = p->vrows;
+
+ ypos += p->yscroll;
+ return(ypos < rows ? ypos : ypos-rows);
+}
+
+
+static int fbcon_clear(struct vc_data *conp, int sy, int sx, int height,
+ int width)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+ u_int y_break;
+
+ if (!p->can_soft_blank && console_blanked)
+ return(0);
+
+ if ((sy <= p->cursor_y) && (p->cursor_y < sy+height) &&
+ (sx <= p->cursor_x) && (p->cursor_x < sx+width))
+ CURSOR_UNDRAWN();
+
+ /* Split blits that cross physical y_wrap boundary */
+
+ y_break = p->vrows-p->yscroll;
+ if (sy < y_break && sy+height-1 >= y_break) {
+ u_int b = y_break-sy;
+ p->dispsw->clear(conp, p, real_y(p, sy), sx, b, width);
+ p->dispsw->clear(conp, p, real_y(p, sy+b), sx, height-b, width);
+ } else
+ p->dispsw->clear(conp, p, real_y(p, sy), sx, height, width);
+
+ return(0);
+}
+
+
+static int fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+
+ if (!p->can_soft_blank && console_blanked)
+ return 0;
+
+ if ((p->cursor_x == xpos) && (p->cursor_y == ypos))
+ CURSOR_UNDRAWN();
+
+ p->dispsw->putc(conp, p, c, real_y(p, ypos), xpos);
+
+ return 0;
+}
+
+
+static int fbcon_putcs(struct vc_data *conp, const char *s, int count,
+ int ypos, int xpos)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+
+ if (!p->can_soft_blank && console_blanked)
+ return 0;
+
+ if ((p->cursor_y == ypos) && (xpos <= p->cursor_x) &&
+ (p->cursor_x < (xpos + count)))
+ CURSOR_UNDRAWN();
+ p->dispsw->putcs(conp, p, s, count, real_y(p, ypos), xpos);
+
+ return(0);
+}
+
+
+static int fbcon_cursor(struct vc_data *conp, int mode)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+
+ /* Avoid flickering if there's no real change. */
+ if (p->cursor_x == conp->vc_x && p->cursor_y == conp->vc_y &&
+ (mode == CM_ERASE) == !cursor_on)
+ return 0;
+ if (CURSOR_UNDRAWN ())
+ p->dispsw->rev_char(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;
+
+ case CM_MOVE:
+ case CM_DRAW:
+ vbl_cursor_cnt = CURSOR_DRAW_DELAY;
+ cursor_on = 1;
+ break;
+ }
+
+ return(0);
+}
+
+
+#if FLASHING_CURSOR
+static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp)
+{
+ struct display *p;
+
+ if (!cursor_on)
+ return;
+
+ 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->rev_char(p, p->cursor_x, real_y(p, p->cursor_y));
+ cursor_drawn ^= 1;
+ vbl_cursor_cnt = cursor_blink_rate;
+ }
+}
+#endif
+
+#if SUPPORT_SCROLLBACK
+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 */
+ p->yscroll -= p->vrows;
+ p->var.xoffset = 0;
+ p->var.yoffset = p->yscroll*p->fontheight;
+ p->var.vmode |= FB_VMODE_YWRAP;
+ p->fb_info->updatevar(unit);
+#if SUPPORT_SCROLLBACK
+ scrollback_max += count;
+ if (scrollback_max > p->vrows-conp->vc_rows)
+ scrollback_max = p->vrows-conp->vc_rows;
+ 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 */
+ p->yscroll += p->vrows;
+ p->var.xoffset = 0;
+ p->var.yoffset = p->yscroll*p->fontheight;
+ p->var.vmode |= FB_VMODE_YWRAP;
+ p->fb_info->updatevar(unit);
+#if SUPPORT_SCROLLBACK
+ scrollback_max -= count;
+ if (scrollback_max < 0)
+ scrollback_max = 0;
+ scrollback_current = 0;
+#endif
+}
+
+
+static __inline__ void ypan_up(int unit, struct vc_data *conp,
+ 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;
+ }
+ p->var.xoffset = 0;
+ p->var.yoffset = p->yscroll*p->fontheight;
+ p->var.vmode &= ~FB_VMODE_YWRAP;
+ p->fb_info->updatevar(unit);
+}
+
+
+static __inline__ void ypan_down(int unit, struct vc_data *conp,
+ struct display *p, int count)
+{
+ p->yscroll -= count;
+ if (p->yscroll < 0) {
+ p->yscroll = p->vrows-conp->vc_rows;
+ p->dispsw->bmove(p, 0, 0, p->yscroll+count, 0, conp->vc_rows-count,
+ conp->vc_cols);
+ }
+ p->var.xoffset = 0;
+ p->var.yoffset = p->yscroll*p->fontheight;
+ p->var.vmode &= ~FB_VMODE_YWRAP;
+ p->fb_info->updatevar(unit);
+}
+
+
+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];
+
+ if (!p->can_soft_blank && console_blanked)
+ return(0);
+
+ fbcon_cursor(conp, CM_ERASE);
+
+ /*
+ * ++Geert: Only use ywrap/ypan if the console is in text mode
+ */
+
+ switch (dir) {
+ case SM_UP:
+ if (count > conp->vc_rows) /* Maximum realistic size */
+ count = conp->vc_rows;
+ if (vt_cons[unit]->vc_mode == KD_TEXT)
+ switch (p->scrollmode) {
+ case SCROLL_YWRAP:
+ if (b-t-count > 3*conp->vc_rows>>2) {
+ 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);
+ } else
+ fbcon_bmove(conp, t+count, 0, t, 0, b-t-count,
+ conp->vc_cols);
+ fbcon_clear(conp, b-count, 0, count, conp->vc_cols);
+ break;
+
+ case SCROLL_YPAN:
+ if (b-t-count > 3*conp->vc_rows>>2) {
+ if (t > 0)
+ fbcon_bmove(conp, 0, 0, count, 0, t,
+ conp->vc_cols);
+ ypan_up(unit, conp, p, count);
+ if (conp->vc_rows-b > 0)
+ fbcon_bmove(conp, b-count, 0, b, 0,
+ conp->vc_rows-b, conp->vc_cols);
+ } else
+ fbcon_bmove(conp, t+count, 0, t, 0, b-t-count,
+ conp->vc_cols);
+ fbcon_clear(conp, b-count, 0, count, conp->vc_cols);
+ break;
+
+ case SCROLL_YMOVE:
+ p->dispsw->bmove(p, t+count, 0, t, 0, b-t-count,
+ conp->vc_cols);
+ p->dispsw->clear(conp, p, b-count, 0, count,
+ conp->vc_cols);
+ break;
+ }
+ else {
+ fbcon_bmove(conp, t+count, 0, t, 0, b-t-count, conp->vc_cols);
+ fbcon_clear(conp, b-count, 0, count, conp->vc_cols);
+ }
+ break;
+
+ case SM_DOWN:
+ if (count > conp->vc_rows) /* Maximum realistic size */
+ count = conp->vc_rows;
+ if (vt_cons[unit]->vc_mode == KD_TEXT)
+ switch (p->scrollmode) {
+ case SCROLL_YWRAP:
+ if (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);
+#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);
+ } else
+ fbcon_bmove(conp, t, 0, t+count, 0, b-t-count,
+ conp->vc_cols);
+ fbcon_clear(conp, t, 0, count, conp->vc_cols);
+ break;
+
+ case SCROLL_YPAN:
+ if (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);
+ ypan_down(unit, conp, p, count);
+ if (t > 0)
+ fbcon_bmove(conp, count, 0, 0, 0, t,
+ conp->vc_cols);
+ } else
+ fbcon_bmove(conp, t, 0, t+count, 0, b-t-count,
+ conp->vc_cols);
+ fbcon_clear(conp, t, 0, count, conp->vc_cols);
+ break;
+
+ case SCROLL_YMOVE:
+ p->dispsw->bmove(p, t, 0, t+count, 0, b-t-count,
+ conp->vc_cols);
+ p->dispsw->clear(conp, p, t, 0, count, conp->vc_cols);
+ break;
+ }
+ else {
+ /*
+ * Fixed bmove() should end Arno's frustration with copying?
+ * Confucius says:
+ * Man who copies in wrong direction, end up with trashed
+ * data
+ */
+ fbcon_bmove(conp, t, 0, t+count, 0, b-t-count, conp->vc_cols);
+ fbcon_clear(conp, t, 0, count, conp->vc_cols);
+ }
+ break;
+
+ case SM_LEFT:
+ fbcon_bmove(conp, 0, t+count, 0, t, conp->vc_rows, b-t-count);
+ fbcon_clear(conp, 0, b-count, conp->vc_rows, count);
+ break;
+
+ case SM_RIGHT:
+ fbcon_bmove(conp, 0, t, 0, t+count, conp->vc_rows, b-t-count);
+ fbcon_clear(conp, 0, t, conp->vc_rows, count);
+ break;
+ }
+
+ return(0);
+}
+
+
+static int fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+
+ if (!p->can_soft_blank && console_blanked)
+ return(0);
+
+ if (((sy <= p->cursor_y) && (p->cursor_y < sy+height) &&
+ (sx <= p->cursor_x) && (p->cursor_x < sx+width)) ||
+ ((dy <= p->cursor_y) && (p->cursor_y < dy+height) &&
+ (dx <= p->cursor_x) && (p->cursor_x < dx+width)))
+ fbcon_cursor(conp, CM_ERASE);
+
+ /* Split blits that cross physical y_wrap case.
+ * Pathological case involves 4 blits, better to use recursive
+ * code rather than unrolled case
+ *
+ * Recursive invocations don't need to erase the cursor over and
+ * over again, so we use fbcon_bmove_rec()
+ */
+ fbcon_bmove_rec(p, sy, sx, dy, dx, height, width, p->vrows-p->yscroll);
+
+ return(0);
+}
+
+
+static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width, u_int y_break)
+{
+ u_int b;
+
+ if (sy < y_break && sy+height > y_break) {
+ b = y_break-sy;
+ if (dy < sy) { /* Avoid trashing self */
+ fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break);
+ fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break);
+ } else {
+ fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break);
+ fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break);
+ }
+ return;
+ }
+
+ if (dy < y_break && dy+height > y_break) {
+ b = y_break-dy;
+ if (dy < sy) { /* Avoid trashing self */
+ fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break);
+ fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break);
+ } else {
+ fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break);
+ fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break);
+ }
+ return;
+ }
+ p->dispsw->bmove(p, real_y(p, sy), sx, real_y(p, dy), dx, height, width);
+}
+
+
+static int fbcon_switch(struct vc_data *conp)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+ struct fb_info *info = p->fb_info;
+
+ if (info && info->switch_con)
+ (*info->switch_con)(conp->vc_num);
+#if SUPPORT_SCROLLBACK
+ scrollback_max = 0;
+ scrollback_current = 0;
+#endif
+ return(0);
+}
+
+
+static int fbcon_blank(int blank)
+{
+ struct display *p = &fb_display[fg_console];
+
+ fbcon_cursor(p->conp, blank ? CM_ERASE : CM_DRAW);
+
+ if (!p->can_soft_blank) {
+ if (blank) {
+ 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);
+ } else {
+ /* Tell console.c that it has to restore the screen itself */
+ return(1);
+ }
+ }
+ (*p->fb_info->blank)(blank);
+ return(0);
+}
+
+
+static int fbcon_get_font(struct vc_data *conp, int *w, int *h, char *data)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+ int i, j, size, alloc;
+
+ size = (p->fontwidth+7)/8 * p->fontheight * 256;
+ alloc = (*w+7)/8 * *h * 256;
+ *w = p->fontwidth;
+ *h = p->fontheight;
+
+ if (alloc < size)
+ /* allocation length not sufficient */
+ 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 );
+}
+
+
+#define REFCOUNT(fd) (((int *)(fd))[-1])
+
+static int fbcon_set_font(struct vc_data *conp, int w, int h, char *data)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+ int i, j, size, userspace = 1, resize;
+ char *old_data = NULL, *new_data;
+
+ if (w < 0)
+ w = p->fontwidth;
+ if (h < 0)
+ h = p->fontheight;
+
+ if (w == 0) {
+ /* engage predefined font, name in 'data' */
+ char name[MAX_FONT_NAME+1];
+
+ if ((i = verify_area( VERIFY_READ, (void *)data, MAX_FONT_NAME )))
+ return i;
+ copy_from_user( name, data, MAX_FONT_NAME );
+ name[sizeof(name)-1] = 0;
+
+ if (!findsoftfont( name, &w, &h, (u_char **)&data ))
+ 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 );
+ if (h == unit)
+ return( 0 ); /* nothing to do */
+ op = &fb_display[h];
+ if (op->fontdata == p->fontdata)
+ return( 0 ); /* already the same font... */
+
+ resize = (op->fontwidth != p->fontwidth) ||
+ (op->fontheight != p->fontheight);
+ if (p->userfont)
+ old_data = p->fontdata;
+ p->fontdata = op->fontdata;
+ w = p->fontwidth = op->fontwidth;
+ h = p->fontheight = op->fontheight;
+ if ((p->userfont = op->userfont))
+ REFCOUNT(p->fontdata)++; /* increment usage counter */
+ goto activate;
+ }
+
+ if (w != 8)
+ /* Currently only fontwidth == 8 supported */
+ return( -ENXIO );
+
+ resize = (w != p->fontwidth) || (h != p->fontheight);
+ size = (w+7)/8 * h * 256;
+
+ if (p->userfont)
+ old_data = p->fontdata;
+
+ if (userspace) {
+ if (!(new_data = kmalloc( sizeof(int)+size, GFP_USER )))
+ return( -ENOMEM );
+ new_data += sizeof(int);
+ REFCOUNT(new_data) = 1; /* usage counter */
+
+ for (i = 0; i < 256; i++)
+ for (j = 0; j < h; j++)
+ new_data[i*h+j] = data[i*32+j];
+
+ p->fontdata = new_data;
+ p->userfont = 1;
+ } else {
+ p->fontdata = data;
+ p->userfont = 0;
+ }
+ p->fontwidth = w;
+ p->fontheight = h;
+
+activate:
+ if (resize) {
+ /* reset wrap/pan */
+ p->var.xoffset = p->var.yoffset = p->yscroll = 0;
+ /* Adjust the virtual screen-size to fontheight*rows */
+ p->var.yres_virtual = (p->var.yres/h)*h;
+ p->vrows = p->var.yres_virtual/h;
+ updatescrollmode(p);
+ vc_resize_con( p->var.yres/h, p->var.xres/w, unit );
+ } else if (unit == fg_console)
+ update_screen( unit );
+
+ if (old_data && (--REFCOUNT(old_data) == 0))
+ kfree( old_data - sizeof(int) );
+
+ return( 0 );
+}
+
+static unsigned short palette_red[16];
+static unsigned short palette_green[16];
+static unsigned short palette_blue[16];
+
+static struct fb_cmap palette_cmap = {
+ 0, 16, palette_red, palette_green, palette_blue, NULL
+};
+
+static int fbcon_set_palette(struct vc_data *conp, unsigned char *table)
+{
+ int unit = conp->vc_num;
+ struct display *p = &fb_display[unit];
+ int i, j, k;
+ u_char val;
+
+ if (!conp->vc_can_do_color || (!p->can_soft_blank && console_blanked))
+ return(-EINVAL);
+ for (i = j = 0; i < 16; i++) {
+ k = table[i];
+ val = conp->vc_palette[j++];
+ palette_red[k] = (val<<8)|val;
+ val = conp->vc_palette[j++];
+ palette_green[k] = (val<<8)|val;
+ val = conp->vc_palette[j++];
+ palette_blue[k] = (val<<8)|val;
+ }
+ palette_cmap.len = 1<<p->var.bits_per_pixel;
+ if (palette_cmap.len > 16)
+ palette_cmap.len = 16;
+ return(p->fb_info->setcmap(&palette_cmap, unit));
+}
+
+static int fbcon_scrolldelta(int lines)
+{
+#if SUPPORT_SCROLLBACK
+ int unit = fg_console; /* xxx */
+ struct display *p = &fb_display[unit];
+ int offset;
+
+ if (!p->can_soft_blank && console_blanked ||
+ vt_cons[unit]->vc_mode != KD_TEXT || !lines ||
+ p->scrollmode != SCROLL_YWRAP)
+ return 0;
+
+ fbcon_cursor(conp, CM_ERASE);
+
+ scrollback_current -= lines;
+ if (scrollback_current < 0)
+ scrollback_current = 0;
+ else if (scrollback_current > scrollback_max)
+ scrollback_current = scrollback_max;
+
+ offset = p->yscroll-scrollback_current;
+ if (offset < 0)
+ offset += p->vrows;
+ else if (offset > p->vrows)
+ offset -= p->vrows;
+ p->var.vmode |= FB_VMODE_YWRAP;
+ p->var.xoffset = 0;
+ p->var.yoffset = offset*p->fontheight;
+ p->fb_info->updatevar(unit);
+#else
+ return -ENOSYS;
+#endif
+}
+
+
+#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 */
+ int depth = p->var.bits_per_pixel;
+ int line = p->next_line;
+ unsigned char *fb = p->screen_base;
+ unsigned char *logo;
+ unsigned char *dst, *src;
+ int i, j, n, x1, y1;
+ int logo_depth, done = 0;
+
+ /* Set colors if visual is PSEUDOCOLOR and we have enough colors */
+ if (p->visual == FB_VISUAL_PSEUDOCOLOR && depth >= 4) {
+ int first_col = depth >= 8 ? 32 : depth > 4 ? 16 : 0;
+ int num_cols = depth >= 8 ? LINUX_LOGO_COLORS : 16;
+ unsigned char *red, *green, *blue;
+ int old_cmap_len;
+
+ if (depth >= 8) {
+ red = linux_logo_red;
+ green = linux_logo_green;
+ blue = linux_logo_blue;
+ }
+ else {
+ red = linux_logo16_red;
+ green = linux_logo16_green;
+ blue = linux_logo16_blue;
+ }
+
+ /* dirty trick to avoid setcmap calling kmalloc which isn't
+ * initialized yet... */
+ old_cmap_len = fb_display[fg_console].cmap.len;
+ fb_display[fg_console].cmap.len = 1 << depth;
+
+ for( i = 0; i < num_cols; i += n ) {
+ n = num_cols - i;
+ if (n > 16)
+ /* palette_cmap provides space for only 16 colors at once */
+ n = 16;
+ palette_cmap.start = first_col + i;
+ palette_cmap.len = n;
+ for( j = 0; j < n; ++j ) {
+ palette_cmap.red[j] = (red[i+j] << 8) | red[i+j];
+ palette_cmap.green[j] = (green[i+j] << 8) | green[i+j];
+ palette_cmap.blue[j] = (blue[i+j] << 8) | blue[i+j];
+ }
+ p->fb_info->setcmap( &palette_cmap, fg_console );
+ }
+ fb_display[fg_console].cmap.len = old_cmap_len;
+ }
+
+ if (depth >= 8) {
+ logo = linux_logo;
+ logo_depth = 8;
+ }
+ else if (depth >= 4) {
+ logo = linux_logo16;
+ logo_depth = 4;
+ }
+ else {
+ logo = linux_logo_bw;
+ logo_depth = 1;
+ }
+
+#if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CYBER) || \
+ defined(CONFIG_FBCON_RETINAZ3)
+ if ((depth % 8 == 0) && (p->visual == FB_VISUAL_TRUECOLOR ||
+ p->visual == FB_VISUAL_DIRECTCOLOR)) {
+ /* Modes without color mapping, needs special data transformation... */
+ unsigned long 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] & redmask) << redshift) |
+ ((linux_logo_green[*src] & greenmask) << greenshift) |
+ ((linux_logo_blue[*src] & bluemask) << blueshift);
+ for( i = 0; i < bdepth; ++i )
+ *dst++ = val >> (i*8);
+ }
+ }
+
+ done = 1;
+ }
+#endif
+#if defined(CONFIG_FBCON_CFB8) || defined(CONFIG_FBCON_CYBER) || \
+ defined(CONFIG_FBCON_RETINAZ3)
+ 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++;
+ }
+ }
+
+ 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;
+
+ /* 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 */
+#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;
+ }
+ *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;
+ }
+ }
+ }
+
+ done = 1;
+ }
+#endif
+#if defined(CONFIG_FBCON_MFB) || defined(CONFIG_FBCON_AFB) || \
+ defined(CONFIG_FBCON_ILBM)
+ if (depth == 1) {
+ /* 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
+ /* Modes not yet supported: packed pixels with depth != 8 (does such a
+ * thing exist in reality?) */
+
+ return( done ? LOGO_H/p->fontheight + 1 : 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
+};
+
+
+/*
+ * Driver registration
+ */
+
+static struct display_switch *drivers = NULL, *accel_drivers = NULL;
+
+int fbcon_register_driver(struct display_switch *dispsw, int is_accel)
+{
+ struct display_switch **list;
+
+ list = is_accel ? &accel_drivers : &drivers;
+ dispsw->next = *list;
+ *list = dispsw;
+ return 0;
+}
+
+int fbcon_unregister_driver(struct display_switch *dispsw)
+{
+ struct display_switch **list;
+
+ for (list = &accel_drivers; *list; list = &(*list)->next)
+ if (*list == dispsw) {
+ *list = dispsw->next;
+ dispsw->next = NULL;
+ return 0;
+ }
+ for (list = &drivers; *list; list = &(*list)->next)
+ if (*list == dispsw) {
+ *list = dispsw->next;
+ dispsw->next = NULL;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+
+static struct display_switch *probe_list(struct display_switch *dispsw,
+ struct display *disp)
+{
+ while (dispsw) {
+ if (!dispsw->open(disp))
+ return(dispsw);
+ dispsw = dispsw->next;
+ }
+ return(NULL);
+}
+
+
+#ifdef CONFIG_KERNELD
+static void request_driver(struct display *disp, int is_accel)
+{
+ char modname[30];
+ int len;
+ const char *type;
+
+ if (disp->var.bits_per_pixel == 1)
+ type = "mfb";
+ else
+ switch (disp->type) {
+ case FB_TYPE_INTERLEAVED_PLANES:
+ if (disp->type_aux == 2)
+ type = "iplan2p%d";
+ else
+ type = "ilbm";
+ break;
+ case FB_TYPE_PLANES:
+ type = "afb";
+ break;
+ case FB_TYPE_PACKED_PIXELS:
+ type = "cfb%d";
+ break;
+ default:
+ return;
+ }
+ len = sprintf(modname, "fbcon-");
+ len += sprintf(modname+len, type, disp->var.bits_per_pixel);
+ if (is_accel)
+ len += sprintf(modname+len, "-%d", disp->var.accel);
+ request_module(modname);
+}
+#endif /* CONFIG_KERNELD */
+
+
+static struct display_switch *fbcon_get_driver(struct display *disp)
+{
+ struct display_switch *dispsw;
+
+ if (disp->var.accel != FB_ACCEL_NONE) {
+ /* First try an accelerated driver */
+ dispsw = probe_list(accel_drivers, disp);
+#ifdef CONFIG_KERNELD
+ if (!dispsw) {
+ request_driver(disp, 1);
+ dispsw = probe_list(accel_drivers, disp);
+ }
+#endif
+ if (dispsw)
+ return(dispsw);
+ }
+
+ /* Then try an unaccelerated driver */
+ dispsw = probe_list(drivers, disp);
+#ifdef CONFIG_KERNELD
+ if (!dispsw) {
+ request_driver(disp, 0);
+ dispsw = probe_list(drivers, disp);
+ }
+#endif
+ return(dispsw);
+}
+
+
+/*
+ * Dummy Low Level Operations
+ */
+
+static int open_dummy(struct display *p)
+{
+ if (p->line_length)
+ p->next_line = p->line_length;
+ else
+ p->next_line = p->var.xres_virtual>>3;
+ p->next_plane = 0;
+ p->var.bits_per_pixel = 1;
+ return 0;
+}
+
+static void misc_dummy(void) {}
+
+static struct display_switch dispsw_dummy = {
+ open_dummy,
+ /* release_dummy */
+ misc_dummy,
+ /* bmove_dummy */
+ (void (*)(struct display *, int, int, int, int, int, int))misc_dummy,
+ /* clear_dummy */
+ (void (*)(struct vc_data *, struct display *, int, int, int, int))misc_dummy,
+ /* putc_dummy */
+ (void (*)(struct vc_data *, struct display *, int, int, int))misc_dummy,
+ /* putcs_dummy */
+ (void (*)(struct vc_data *, struct display *, const char *, int, int, int))misc_dummy,
+ /* rev_char_dummy */
+ (void (*)(struct display *, int, int))misc_dummy,
+};
+
+
+/*
+ * Visible symbols for modules
+ */
+
+EXPORT_SYMBOL(fb_display);
+EXPORT_SYMBOL(fbcon_register_driver);
+EXPORT_SYMBOL(fbcon_unregister_driver);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov