Linux 2.4.0-test9pre8
[davej-history.git] / drivers / video / fbcon.c
blobc559afcca0a9e0b60a604075fc3f8a2b548cbd9f
1 /*
2 * linux/drivers/video/fbcon.c -- Low level frame buffer based console driver
4 * Copyright (C) 1995 Geert Uytterhoeven
7 * This file is based on the original Amiga console driver (amicon.c):
9 * Copyright (C) 1993 Hamish Macdonald
10 * Greg Harp
11 * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk]
13 * with work by William Rucklidge (wjr@cs.cornell.edu)
14 * Geert Uytterhoeven
15 * Jes Sorensen (jds@kom.auc.dk)
16 * Martin Apel
18 * and on the original Atari console driver (atacon.c):
20 * Copyright (C) 1993 Bjoern Brauel
21 * Roman Hodek
23 * with work by Guenther Kelleter
24 * Martin Schaller
25 * Andreas Schwab
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)
32 * Random hacking by Martin Mares <mj@ucw.cz>
35 * The low level operations for the various display memory organizations are
36 * now in separate source files.
38 * Currently the following organizations are supported:
40 * o afb Amiga bitplanes
41 * o cfb{2,4,8,16,24,32} Packed pixels
42 * o ilbm Amiga interleaved bitplanes
43 * o iplan2p[248] Atari interleaved bitplanes
44 * o mfb Monochrome
45 * o vga VGA characters/attributes
47 * To do:
49 * - Implement 16 plane mode (iplan2p16)
52 * This file is subject to the terms and conditions of the GNU General Public
53 * License. See the file COPYING in the main directory of this archive for
54 * more details.
57 #undef FBCONDEBUG
59 #include <linux/config.h>
60 #include <linux/module.h>
61 #include <linux/types.h>
62 #include <linux/sched.h>
63 #include <linux/fs.h>
64 #include <linux/kernel.h>
65 #include <linux/delay.h> /* MSch: for IRQ probe */
66 #include <linux/tty.h>
67 #include <linux/console.h>
68 #include <linux/string.h>
69 #include <linux/kd.h>
70 #include <linux/malloc.h>
71 #include <linux/fb.h>
72 #include <linux/vt_kern.h>
73 #include <linux/selection.h>
74 #include <linux/smp.h>
75 #include <linux/init.h>
77 #include <asm/irq.h>
78 #include <asm/system.h>
79 #include <asm/uaccess.h>
80 #ifdef CONFIG_AMIGA
81 #include <asm/amigahw.h>
82 #include <asm/amigaints.h>
83 #endif /* CONFIG_AMIGA */
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 #ifdef CONFIG_FBCON_VGA_PLANES
95 #include <asm/io.h>
96 #endif
97 #define INCLUDE_LINUX_LOGO_DATA
98 #include <asm/linux_logo.h>
100 #include <video/fbcon.h>
101 #include <video/fbcon-mac.h> /* for 6x11 font on mac */
102 #include <video/font.h>
104 #ifdef FBCONDEBUG
105 # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
106 #else
107 # define DPRINTK(fmt, args...)
108 #endif
110 #define LOGO_H 80
111 #define LOGO_W 80
112 #define LOGO_LINE (LOGO_W/8)
114 struct display fb_display[MAX_NR_CONSOLES];
115 char con2fb_map[MAX_NR_CONSOLES];
116 static int logo_lines;
117 static int logo_shown = -1;
118 /* Software scrollback */
119 int fbcon_softback_size = 32768;
120 static unsigned long softback_buf, softback_curr;
121 static unsigned long softback_in;
122 static unsigned long softback_top, softback_end;
123 static int softback_lines;
125 #define REFCOUNT(fd) (((int *)(fd))[-1])
126 #define FNTSIZE(fd) (((int *)(fd))[-2])
127 #define FNTCHARCNT(fd) (((int *)(fd))[-3])
128 #define FNTSUM(fd) (((int *)(fd))[-4])
129 #define FONT_EXTRA_WORDS 4
131 #define CM_SOFTBACK (8)
133 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * conp->vc_size_row)
135 static void fbcon_free_font(struct display *);
136 static int fbcon_set_origin(struct vc_data *);
139 * Emmanuel: fbcon will now use a hardware cursor if the
140 * low-level driver provides a non-NULL dispsw->cursor pointer,
141 * in which case the hardware should do blinking, etc.
143 * if dispsw->cursor is NULL, use Atari alike software cursor
146 static int cursor_drawn;
148 #define CURSOR_DRAW_DELAY (1)
150 /* # VBL ints between cursor state changes */
151 #define ARM_CURSOR_BLINK_RATE (10)
152 #define AMIGA_CURSOR_BLINK_RATE (20)
153 #define ATARI_CURSOR_BLINK_RATE (42)
154 #define MAC_CURSOR_BLINK_RATE (32)
155 #define DEFAULT_CURSOR_BLINK_RATE (20)
157 static int vbl_cursor_cnt;
158 static int cursor_on;
159 static int cursor_blink_rate;
161 static inline void cursor_undrawn(void)
163 vbl_cursor_cnt = 0;
164 cursor_drawn = 0;
168 #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1)
172 * Interface used by the world
175 static const char *fbcon_startup(void);
176 static void fbcon_init(struct vc_data *conp, int init);
177 static void fbcon_deinit(struct vc_data *conp);
178 static int fbcon_changevar(int con);
179 static void fbcon_clear(struct vc_data *conp, int sy, int sx, int height,
180 int width);
181 static void fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos);
182 static void fbcon_putcs(struct vc_data *conp, const unsigned short *s, int count,
183 int ypos, int xpos);
184 static void fbcon_cursor(struct vc_data *conp, int mode);
185 static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir,
186 int count);
187 static void fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
188 int height, int width);
189 static int fbcon_switch(struct vc_data *conp);
190 static int fbcon_blank(struct vc_data *conp, int blank);
191 static int fbcon_font_op(struct vc_data *conp, struct console_font_op *op);
192 static int fbcon_set_palette(struct vc_data *conp, unsigned char *table);
193 static int fbcon_scrolldelta(struct vc_data *conp, int lines);
197 * Internal routines
200 static void fbcon_setup(int con, int init, int logo);
201 static __inline__ int real_y(struct display *p, int ypos);
202 static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp);
203 static __inline__ void updatescrollmode(struct display *p);
204 static __inline__ void ywrap_up(int unit, struct vc_data *conp,
205 struct display *p, int count);
206 static __inline__ void ywrap_down(int unit, struct vc_data *conp,
207 struct display *p, int count);
208 static __inline__ void ypan_up(int unit, struct vc_data *conp,
209 struct display *p, int count);
210 static __inline__ void ypan_down(int unit, struct vc_data *conp,
211 struct display *p, int count);
212 static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx,
213 int height, int width, u_int y_break);
215 static int fbcon_show_logo(void);
217 #ifdef CONFIG_MAC
219 * On the Macintoy, there may or may not be a working VBL int. We need to probe
221 static int vbl_detected;
223 static void fbcon_vbl_detect(int irq, void *dummy, struct pt_regs *fp)
225 vbl_detected++;
227 #endif
229 static void cursor_timer_handler(unsigned long dev_addr);
231 static struct timer_list cursor_timer = {
232 function: cursor_timer_handler
235 static void cursor_timer_handler(unsigned long dev_addr)
237 fbcon_vbl_handler(0, NULL, NULL);
238 cursor_timer.expires = jiffies+HZ/50;
239 add_timer(&cursor_timer);
242 int PROC_CONSOLE(const struct fb_info *info)
244 int fgc;
246 if (info->display_fg != NULL)
247 fgc = info->display_fg->vc_num;
248 else
249 return -1;
251 if (!current->tty)
252 return fgc;
254 if (current->tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
255 /* XXX Should report error here? */
256 return fgc;
258 if (MINOR(current->tty->device) < 1)
259 return fgc;
261 return MINOR(current->tty->device) - 1;
264 int set_all_vcs(int fbidx, struct fb_ops *fb, struct fb_var_screeninfo *var,
265 struct fb_info *info)
267 int unit, err;
269 var->activate |= FB_ACTIVATE_TEST;
270 err = fb->fb_set_var(var, PROC_CONSOLE(info), info);
271 var->activate &= ~FB_ACTIVATE_TEST;
272 if (err)
273 return err;
274 for (unit = 0; unit < MAX_NR_CONSOLES; unit++)
275 if (fb_display[unit].conp && con2fb_map[unit] == fbidx)
276 fb->fb_set_var(var, unit, info);
277 return 0;
280 void set_con2fb_map(int unit, int newidx)
282 int oldidx = con2fb_map[unit];
283 struct fb_info *oldfb, *newfb;
284 struct vc_data *conp;
285 char *fontdata;
286 unsigned short fontwidth, fontheight, fontwidthlog, fontheightlog;
287 int userfont;
289 if (newidx != con2fb_map[unit]) {
290 oldfb = registered_fb[oldidx];
291 newfb = registered_fb[newidx];
292 if (newfb->fbops->owner)
293 __MOD_INC_USE_COUNT(newfb->fbops->owner);
294 if (newfb->fbops->fb_open && newfb->fbops->fb_open(newfb,0)) {
295 if (newfb->fbops->owner)
296 __MOD_DEC_USE_COUNT(newfb->fbops->owner);
297 return;
299 if (oldfb->fbops->fb_release)
300 oldfb->fbops->fb_release(oldfb,0);
301 if (oldfb->fbops->owner)
302 __MOD_DEC_USE_COUNT(oldfb->fbops->owner);
303 conp = fb_display[unit].conp;
304 fontdata = fb_display[unit].fontdata;
305 fontwidth = fb_display[unit]._fontwidth;
306 fontheight = fb_display[unit]._fontheight;
307 fontwidthlog = fb_display[unit]._fontwidthlog;
308 fontheightlog = fb_display[unit]._fontheightlog;
309 userfont = fb_display[unit].userfont;
310 con2fb_map[unit] = newidx;
311 fb_display[unit] = *(newfb->disp);
312 fb_display[unit].conp = conp;
313 fb_display[unit].fontdata = fontdata;
314 fb_display[unit]._fontwidth = fontwidth;
315 fb_display[unit]._fontheight = fontheight;
316 fb_display[unit]._fontwidthlog = fontwidthlog;
317 fb_display[unit]._fontheightlog = fontheightlog;
318 fb_display[unit].userfont = userfont;
319 fb_display[unit].fb_info = newfb;
320 if (conp)
321 conp->vc_display_fg = &newfb->display_fg;
322 if (!newfb->display_fg)
323 newfb->display_fg = conp;
324 if (!newfb->changevar)
325 newfb->changevar = oldfb->changevar;
326 /* tell console var has changed */
327 if (newfb->changevar)
328 newfb->changevar(unit);
333 * Low Level Operations
336 struct display_switch fbcon_dummy;
338 /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */
340 static const char *fbcon_startup(void)
342 const char *display_desc = "frame buffer device";
343 int irqres = 1;
344 static int done = 0;
347 * If num_registered_fb is zero, this is a call for the dummy part.
348 * The frame buffer devices weren't initialized yet.
350 if (!num_registered_fb || done)
351 return display_desc;
352 done = 1;
354 #ifdef CONFIG_AMIGA
355 if (MACH_IS_AMIGA) {
356 cursor_blink_rate = AMIGA_CURSOR_BLINK_RATE;
357 irqres = request_irq(IRQ_AMIGA_VERTB, fbcon_vbl_handler, 0,
358 "console/cursor", fbcon_vbl_handler);
360 #endif /* CONFIG_AMIGA */
361 #ifdef CONFIG_ATARI
362 if (MACH_IS_ATARI) {
363 cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
364 irqres = request_irq(IRQ_AUTO_4, fbcon_vbl_handler, IRQ_TYPE_PRIO,
365 "console/cursor", fbcon_vbl_handler);
367 #endif /* CONFIG_ATARI */
369 #ifdef CONFIG_MAC
371 * On a Macintoy, the VBL interrupt may or may not be active.
372 * As interrupt based cursor is more reliable and race free, we
373 * probe for VBL interrupts.
375 if (MACH_IS_MAC) {
376 int ct = 0;
378 * Probe for VBL: set temp. handler ...
380 irqres = request_irq(IRQ_MAC_VBL, fbcon_vbl_detect, 0,
381 "console/cursor", fbcon_vbl_detect);
382 vbl_detected = 0;
385 * ... and spin for 20 ms ...
387 while (!vbl_detected && ++ct<1000)
388 udelay(20);
390 if(ct==1000)
391 printk("fbcon_startup: No VBL detected, using timer based cursor.\n");
393 free_irq(IRQ_MAC_VBL, fbcon_vbl_detect);
395 if (vbl_detected) {
397 * interrupt based cursor ok
399 cursor_blink_rate = MAC_CURSOR_BLINK_RATE;
400 irqres = request_irq(IRQ_MAC_VBL, fbcon_vbl_handler, 0,
401 "console/cursor", fbcon_vbl_handler);
402 } else {
404 * VBL not detected: fall through, use timer based cursor
406 irqres = 1;
409 #endif /* CONFIG_MAC */
411 #if defined(__arm__) && defined(IRQ_VSYNCPULSE)
412 cursor_blink_rate = ARM_CURSOR_BLINK_RATE;
413 irqres = request_irq(IRQ_VSYNCPULSE, fbcon_vbl_handler, SA_SHIRQ,
414 "console/cursor", fbcon_vbl_handler);
415 #endif
417 if (irqres) {
418 cursor_blink_rate = DEFAULT_CURSOR_BLINK_RATE;
419 cursor_timer.expires = jiffies+HZ/50;
420 add_timer(&cursor_timer);
423 return display_desc;
427 static void fbcon_init(struct vc_data *conp, int init)
429 int unit = conp->vc_num;
430 struct fb_info *info;
432 /* on which frame buffer will we open this console? */
433 info = registered_fb[(int)con2fb_map[unit]];
435 info->changevar = &fbcon_changevar;
436 fb_display[unit] = *(info->disp); /* copy from default */
437 DPRINTK("mode: %s\n",info->modename);
438 DPRINTK("visual: %d\n",fb_display[unit].visual);
439 DPRINTK("res: %dx%d-%d\n",fb_display[unit].var.xres,
440 fb_display[unit].var.yres,
441 fb_display[unit].var.bits_per_pixel);
442 fb_display[unit].conp = conp;
443 fb_display[unit].fb_info = info;
444 /* clear out the cmap so we don't have dangling pointers */
445 fb_display[unit].cmap.len = 0;
446 fb_display[unit].cmap.red = 0;
447 fb_display[unit].cmap.green = 0;
448 fb_display[unit].cmap.blue = 0;
449 fb_display[unit].cmap.transp = 0;
450 fbcon_setup(unit, init, !init);
451 /* Must be done after fbcon_setup to prevent excess updates */
452 conp->vc_display_fg = &info->display_fg;
453 if (!info->display_fg)
454 info->display_fg = conp;
458 static void fbcon_deinit(struct vc_data *conp)
460 int unit = conp->vc_num;
461 struct display *p = &fb_display[unit];
463 fbcon_free_font(p);
464 p->dispsw = &fbcon_dummy;
465 p->conp = 0;
469 static int fbcon_changevar(int con)
471 if (fb_display[con].conp)
472 fbcon_setup(con, 0, 0);
473 return 0;
477 static __inline__ void updatescrollmode(struct display *p)
479 int m;
480 if (p->scrollmode & __SCROLL_YFIXED)
481 return;
482 if (divides(p->ywrapstep, fontheight(p)) &&
483 divides(fontheight(p), p->var.yres_virtual))
484 m = __SCROLL_YWRAP;
485 else if (divides(p->ypanstep, fontheight(p)) &&
486 p->var.yres_virtual >= p->var.yres+fontheight(p))
487 m = __SCROLL_YPAN;
488 else if (p->scrollmode & __SCROLL_YNOMOVE)
489 m = __SCROLL_YREDRAW;
490 else
491 m = __SCROLL_YMOVE;
492 p->scrollmode = (p->scrollmode & ~__SCROLL_YMASK) | m;
495 static void fbcon_font_widths(struct display *p)
497 int i;
499 p->_fontwidthlog = 0;
500 for (i = 2; i <= 6; i++)
501 if (fontwidth(p) == (1 << i))
502 p->_fontwidthlog = i;
503 p->_fontheightlog = 0;
504 for (i = 2; i <= 6; i++)
505 if (fontheight(p) == (1 << i))
506 p->_fontheightlog = i;
509 #define fontwidthvalid(p,w) ((p)->dispsw->fontwidthmask & FONTWIDTH(w))
511 static void fbcon_setup(int con, int init, int logo)
513 struct display *p = &fb_display[con];
514 struct vc_data *conp = p->conp;
515 int nr_rows, nr_cols;
516 int old_rows, old_cols;
517 unsigned short *save = NULL, *r, *q;
518 int i, charcnt = 256;
519 struct fbcon_font_desc *font;
521 if (con != fg_console || (p->fb_info->flags & FBINFO_FLAG_MODULE) ||
522 p->type == FB_TYPE_TEXT)
523 logo = 0;
525 p->var.xoffset = p->var.yoffset = p->yscroll = 0; /* reset wrap/pan */
527 if (con == fg_console && p->type != FB_TYPE_TEXT) {
528 if (fbcon_softback_size) {
529 if (!softback_buf) {
530 softback_buf = (unsigned long)kmalloc(fbcon_softback_size, GFP_KERNEL);
531 if (!softback_buf) {
532 fbcon_softback_size = 0;
533 softback_top = 0;
536 } else {
537 if (softback_buf) {
538 kfree((void *)softback_buf);
539 softback_buf = 0;
540 softback_top = 0;
543 if (softback_buf)
544 softback_in = softback_top = softback_curr = softback_buf;
545 softback_lines = 0;
548 for (i = 0; i < MAX_NR_CONSOLES; i++)
549 if (i != con && fb_display[i].fb_info == p->fb_info &&
550 fb_display[i].conp && fb_display[i].fontdata)
551 break;
553 fbcon_free_font(p);
554 if (i < MAX_NR_CONSOLES) {
555 struct display *q = &fb_display[i];
557 if (fontwidthvalid(p,fontwidth(q))) {
558 /* If we are not the first console on this
559 fb, copy the font from that console */
560 p->_fontwidth = q->_fontwidth;
561 p->_fontheight = q->_fontheight;
562 p->_fontwidthlog = q->_fontwidthlog;
563 p->_fontheightlog = q->_fontheightlog;
564 p->fontdata = q->fontdata;
565 p->userfont = q->userfont;
566 if (p->userfont) {
567 REFCOUNT(p->fontdata)++;
568 charcnt = FNTCHARCNT(p->fontdata);
570 con_copy_unimap(con, i);
574 if (!p->fontdata) {
575 if (!p->fb_info->fontname[0] ||
576 !(font = fbcon_find_font(p->fb_info->fontname)))
577 font = fbcon_get_default_font(p->var.xres, p->var.yres);
578 p->_fontwidth = font->width;
579 p->_fontheight = font->height;
580 p->fontdata = font->data;
581 fbcon_font_widths(p);
584 if (!fontwidthvalid(p,fontwidth(p))) {
585 #ifdef CONFIG_MAC
586 if (MACH_IS_MAC)
587 /* ++Geert: hack to make 6x11 fonts work on mac */
588 p->dispsw = &fbcon_mac;
589 else
590 #endif
592 /* ++Geert: changed from panic() to `correct and continue' */
593 printk(KERN_ERR "fbcon_setup: No support for fontwidth %d\n", fontwidth(p));
594 p->dispsw = &fbcon_dummy;
597 if (p->dispsw->set_font)
598 p->dispsw->set_font(p, fontwidth(p), fontheight(p));
599 updatescrollmode(p);
601 old_cols = conp->vc_cols;
602 old_rows = conp->vc_rows;
604 nr_cols = p->var.xres/fontwidth(p);
605 nr_rows = p->var.yres/fontheight(p);
607 if (logo) {
608 /* Need to make room for the logo */
609 int cnt;
610 int step;
612 logo_lines = (LOGO_H + fontheight(p) - 1) / fontheight(p);
613 q = (unsigned short *)(conp->vc_origin + conp->vc_size_row * old_rows);
614 step = logo_lines * old_cols;
615 for (r = q - logo_lines * old_cols; r < q; r++)
616 if (scr_readw(r) != conp->vc_video_erase_char)
617 break;
618 if (r != q && nr_rows >= old_rows + logo_lines) {
619 save = kmalloc(logo_lines * nr_cols * 2, GFP_KERNEL);
620 if (save) {
621 int i = old_cols < nr_cols ? old_cols : nr_cols;
622 scr_memsetw(save, conp->vc_video_erase_char, logo_lines * nr_cols * 2);
623 r = q - step;
624 for (cnt = 0; cnt < logo_lines; cnt++, r += i)
625 scr_memcpyw_from(save + cnt * nr_cols, r, 2 * i);
626 r = q;
629 if (r == q) {
630 /* We can scroll screen down */
631 r = q - step - old_cols;
632 for (cnt = old_rows - logo_lines; cnt > 0; cnt--) {
633 scr_memcpyw(r + step, r, conp->vc_size_row);
634 r -= old_cols;
636 if (!save) {
637 conp->vc_y += logo_lines;
638 conp->vc_pos += logo_lines * conp->vc_size_row;
641 scr_memsetw((unsigned short *)conp->vc_origin,
642 conp->vc_video_erase_char,
643 conp->vc_size_row * logo_lines);
647 * ++guenther: console.c:vc_allocate() relies on initializing
648 * vc_{cols,rows}, but we must not set those if we are only
649 * resizing the console.
651 if (init) {
652 conp->vc_cols = nr_cols;
653 conp->vc_rows = nr_rows;
655 p->vrows = p->var.yres_virtual/fontheight(p);
656 if ((p->var.yres % fontheight(p)) &&
657 (p->var.yres_virtual % fontheight(p) < p->var.yres % fontheight(p)))
658 p->vrows--;
659 conp->vc_can_do_color = p->var.bits_per_pixel != 1;
660 conp->vc_complement_mask = conp->vc_can_do_color ? 0x7700 : 0x0800;
661 if (charcnt == 256) {
662 conp->vc_hi_font_mask = 0;
663 p->fgshift = 8;
664 p->bgshift = 12;
665 p->charmask = 0xff;
666 } else {
667 conp->vc_hi_font_mask = 0x100;
668 if (conp->vc_can_do_color)
669 conp->vc_complement_mask <<= 1;
670 p->fgshift = 9;
671 p->bgshift = 13;
672 p->charmask = 0x1ff;
675 if (p->dispsw == &fbcon_dummy)
676 printk(KERN_WARNING "fbcon_setup: type %d (aux %d, depth %d) not "
677 "supported\n", p->type, p->type_aux, p->var.bits_per_pixel);
678 p->dispsw->setup(p);
680 p->fgcol = p->var.bits_per_pixel > 2 ? 7 : (1<<p->var.bits_per_pixel)-1;
681 p->bgcol = 0;
683 if (!init) {
684 if (conp->vc_cols != nr_cols || conp->vc_rows != nr_rows)
685 vc_resize_con(nr_rows, nr_cols, con);
686 else if (CON_IS_VISIBLE(conp) &&
687 vt_cons[conp->vc_num]->vc_mode == KD_TEXT) {
688 if (p->dispsw->clear_margins)
689 p->dispsw->clear_margins(conp, p, 0);
690 update_screen(con);
692 if (save) {
693 q = (unsigned short *)(conp->vc_origin + conp->vc_size_row * old_rows);
694 scr_memcpyw(q, save, logo_lines * nr_cols * 2);
695 conp->vc_y += logo_lines;
696 conp->vc_pos += logo_lines * conp->vc_size_row;
697 kfree(save);
701 if (logo) {
702 logo_shown = -2;
703 conp->vc_top = logo_lines;
706 if (con == fg_console && softback_buf) {
707 int l = fbcon_softback_size / conp->vc_size_row;
708 if (l > 5)
709 softback_end = softback_buf + l * conp->vc_size_row;
710 else {
711 /* Smaller scrollback makes no sense, and 0 would screw
712 the operation totally */
713 softback_top = 0;
719 /* ====================================================================== */
721 /* fbcon_XXX routines - interface used by the world
723 * This system is now divided into two levels because of complications
724 * caused by hardware scrolling. Top level functions:
726 * fbcon_bmove(), fbcon_clear(), fbcon_putc()
728 * handles y values in range [0, scr_height-1] that correspond to real
729 * screen positions. y_wrap shift means that first line of bitmap may be
730 * anywhere on this display. These functions convert lineoffsets to
731 * bitmap offsets and deal with the wrap-around case by splitting blits.
733 * fbcon_bmove_physical_8() -- These functions fast implementations
734 * fbcon_clear_physical_8() -- of original fbcon_XXX fns.
735 * fbcon_putc_physical_8() -- (fontwidth != 8) may be added later
737 * WARNING:
739 * At the moment fbcon_putc() cannot blit across vertical wrap boundary
740 * Implies should only really hardware scroll in rows. Only reason for
741 * restriction is simplicity & efficiency at the moment.
744 static __inline__ int real_y(struct display *p, int ypos)
746 int rows = p->vrows;
748 ypos += p->yscroll;
749 return ypos < rows ? ypos : ypos-rows;
753 static void fbcon_clear(struct vc_data *conp, int sy, int sx, int height,
754 int width)
756 int unit = conp->vc_num;
757 struct display *p = &fb_display[unit];
758 u_int y_break;
759 int redraw_cursor = 0;
761 if (!p->can_soft_blank && console_blanked)
762 return;
764 if (!height || !width)
765 return;
767 if ((sy <= p->cursor_y) && (p->cursor_y < sy+height) &&
768 (sx <= p->cursor_x) && (p->cursor_x < sx+width)) {
769 cursor_undrawn();
770 redraw_cursor = 1;
773 /* Split blits that cross physical y_wrap boundary */
775 y_break = p->vrows-p->yscroll;
776 if (sy < y_break && sy+height-1 >= y_break) {
777 u_int b = y_break-sy;
778 p->dispsw->clear(conp, p, real_y(p, sy), sx, b, width);
779 p->dispsw->clear(conp, p, real_y(p, sy+b), sx, height-b, width);
780 } else
781 p->dispsw->clear(conp, p, real_y(p, sy), sx, height, width);
783 if (redraw_cursor)
784 vbl_cursor_cnt = CURSOR_DRAW_DELAY;
788 static void fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos)
790 int unit = conp->vc_num;
791 struct display *p = &fb_display[unit];
792 int redraw_cursor = 0;
794 if (!p->can_soft_blank && console_blanked)
795 return;
797 if (vt_cons[unit]->vc_mode != KD_TEXT)
798 return;
800 if ((p->cursor_x == xpos) && (p->cursor_y == ypos)) {
801 cursor_undrawn();
802 redraw_cursor = 1;
805 p->dispsw->putc(conp, p, c, real_y(p, ypos), xpos);
807 if (redraw_cursor)
808 vbl_cursor_cnt = CURSOR_DRAW_DELAY;
812 static void fbcon_putcs(struct vc_data *conp, const unsigned short *s, int count,
813 int ypos, int xpos)
815 int unit = conp->vc_num;
816 struct display *p = &fb_display[unit];
817 int redraw_cursor = 0;
819 if (!p->can_soft_blank && console_blanked)
820 return;
822 if (vt_cons[unit]->vc_mode != KD_TEXT)
823 return;
825 if ((p->cursor_y == ypos) && (xpos <= p->cursor_x) &&
826 (p->cursor_x < (xpos + count))) {
827 cursor_undrawn();
828 redraw_cursor = 1;
830 p->dispsw->putcs(conp, p, s, count, real_y(p, ypos), xpos);
831 if (redraw_cursor)
832 vbl_cursor_cnt = CURSOR_DRAW_DELAY;
836 static void fbcon_cursor(struct vc_data *conp, int mode)
838 int unit = conp->vc_num;
839 struct display *p = &fb_display[unit];
840 int y = conp->vc_y;
842 if (mode & CM_SOFTBACK) {
843 mode &= ~CM_SOFTBACK;
844 if (softback_lines) {
845 if (y + softback_lines >= conp->vc_rows)
846 mode = CM_ERASE;
847 else
848 y += softback_lines;
850 } else if (softback_lines)
851 fbcon_set_origin(conp);
853 /* do we have a hardware cursor ? */
854 if (p->dispsw->cursor) {
855 p->cursor_x = conp->vc_x;
856 p->cursor_y = y;
857 p->dispsw->cursor(p, mode, p->cursor_x, real_y(p, p->cursor_y));
858 return;
861 /* Avoid flickering if there's no real change. */
862 if (p->cursor_x == conp->vc_x && p->cursor_y == y &&
863 (mode == CM_ERASE) == !cursor_on)
864 return;
866 cursor_on = 0;
867 if (cursor_drawn)
868 p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));
870 p->cursor_x = conp->vc_x;
871 p->cursor_y = y;
873 switch (mode) {
874 case CM_ERASE:
875 cursor_drawn = 0;
876 break;
877 case CM_MOVE:
878 case CM_DRAW:
879 if (cursor_drawn)
880 p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));
881 vbl_cursor_cnt = CURSOR_DRAW_DELAY;
882 cursor_on = 1;
883 break;
888 static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp)
890 struct display *p;
892 if (!cursor_on)
893 return;
895 if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
896 p = &fb_display[fg_console];
897 if (p->dispsw->revc)
898 p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));
899 cursor_drawn ^= 1;
900 vbl_cursor_cnt = cursor_blink_rate;
904 static int scrollback_phys_max = 0;
905 static int scrollback_max = 0;
906 static int scrollback_current = 0;
908 static __inline__ void ywrap_up(int unit, struct vc_data *conp,
909 struct display *p, int count)
911 p->yscroll += count;
912 if (p->yscroll >= p->vrows) /* Deal with wrap */
913 p->yscroll -= p->vrows;
914 p->var.xoffset = 0;
915 p->var.yoffset = p->yscroll*fontheight(p);
916 p->var.vmode |= FB_VMODE_YWRAP;
917 p->fb_info->updatevar(unit, p->fb_info);
918 scrollback_max += count;
919 if (scrollback_max > scrollback_phys_max)
920 scrollback_max = scrollback_phys_max;
921 scrollback_current = 0;
925 static __inline__ void ywrap_down(int unit, struct vc_data *conp,
926 struct display *p, int count)
928 p->yscroll -= count;
929 if (p->yscroll < 0) /* Deal with wrap */
930 p->yscroll += p->vrows;
931 p->var.xoffset = 0;
932 p->var.yoffset = p->yscroll*fontheight(p);
933 p->var.vmode |= FB_VMODE_YWRAP;
934 p->fb_info->updatevar(unit, p->fb_info);
935 scrollback_max -= count;
936 if (scrollback_max < 0)
937 scrollback_max = 0;
938 scrollback_current = 0;
942 static __inline__ void ypan_up(int unit, struct vc_data *conp,
943 struct display *p, int count)
945 p->yscroll += count;
946 if (p->yscroll > p->vrows-conp->vc_rows) {
947 p->dispsw->bmove(p, p->vrows-conp->vc_rows, 0, 0, 0,
948 conp->vc_rows, conp->vc_cols);
949 p->yscroll -= p->vrows-conp->vc_rows;
951 p->var.xoffset = 0;
952 p->var.yoffset = p->yscroll*fontheight(p);
953 p->var.vmode &= ~FB_VMODE_YWRAP;
954 p->fb_info->updatevar(unit, p->fb_info);
955 if (p->dispsw->clear_margins)
956 p->dispsw->clear_margins(conp, p, 1);
957 scrollback_max += count;
958 if (scrollback_max > scrollback_phys_max)
959 scrollback_max = scrollback_phys_max;
960 scrollback_current = 0;
964 static __inline__ void ypan_down(int unit, struct vc_data *conp,
965 struct display *p, int count)
967 p->yscroll -= count;
968 if (p->yscroll < 0) {
969 p->dispsw->bmove(p, 0, 0, p->vrows-conp->vc_rows, 0,
970 conp->vc_rows, conp->vc_cols);
971 p->yscroll += p->vrows-conp->vc_rows;
973 p->var.xoffset = 0;
974 p->var.yoffset = p->yscroll*fontheight(p);
975 p->var.vmode &= ~FB_VMODE_YWRAP;
976 p->fb_info->updatevar(unit, p->fb_info);
977 if (p->dispsw->clear_margins)
978 p->dispsw->clear_margins(conp, p, 1);
979 scrollback_max -= count;
980 if (scrollback_max < 0)
981 scrollback_max = 0;
982 scrollback_current = 0;
985 static void fbcon_redraw_softback(struct vc_data *conp, struct display *p, long delta)
987 unsigned short *d, *s;
988 unsigned long n;
989 int line = 0;
990 int count = conp->vc_rows;
992 d = (u16 *)softback_curr;
993 if (d == (u16 *)softback_in)
994 d = (u16 *)conp->vc_origin;
995 n = softback_curr + delta * conp->vc_size_row;
996 softback_lines -= delta;
997 if (delta < 0) {
998 if (softback_curr < softback_top && n < softback_buf) {
999 n += softback_end - softback_buf;
1000 if (n < softback_top) {
1001 softback_lines -= (softback_top - n) / conp->vc_size_row;
1002 n = softback_top;
1004 } else if (softback_curr >= softback_top && n < softback_top) {
1005 softback_lines -= (softback_top - n) / conp->vc_size_row;
1006 n = softback_top;
1008 } else {
1009 if (softback_curr > softback_in && n >= softback_end) {
1010 n += softback_buf - softback_end;
1011 if (n > softback_in) {
1012 n = softback_in;
1013 softback_lines = 0;
1015 } else if (softback_curr <= softback_in && n > softback_in) {
1016 n = softback_in;
1017 softback_lines = 0;
1020 if (n == softback_curr)
1021 return;
1022 softback_curr = n;
1023 s = (u16 *)softback_curr;
1024 if (s == (u16 *)softback_in)
1025 s = (u16 *)conp->vc_origin;
1026 while (count--) {
1027 unsigned short *start;
1028 unsigned short *le;
1029 unsigned short c;
1030 int x = 0;
1031 unsigned short attr = 1;
1033 start = s;
1034 le = advance_row(s, 1);
1035 do {
1036 c = scr_readw(s);
1037 if (attr != (c & 0xff00)) {
1038 attr = c & 0xff00;
1039 if (s > start) {
1040 p->dispsw->putcs(conp, p, start, s - start,
1041 real_y(p, line), x);
1042 x += s - start;
1043 start = s;
1046 if (c == scr_readw(d)) {
1047 if (s > start) {
1048 p->dispsw->putcs(conp, p, start, s - start,
1049 real_y(p, line), x);
1050 x += s - start + 1;
1051 start = s + 1;
1052 } else {
1053 x++;
1054 start++;
1057 s++;
1058 d++;
1059 } while (s < le);
1060 if (s > start)
1061 p->dispsw->putcs(conp, p, start, s - start, real_y(p, line), x);
1062 line++;
1063 if (d == (u16 *)softback_end)
1064 d = (u16 *)softback_buf;
1065 if (d == (u16 *)softback_in)
1066 d = (u16 *)conp->vc_origin;
1067 if (s == (u16 *)softback_end)
1068 s = (u16 *)softback_buf;
1069 if (s == (u16 *)softback_in)
1070 s = (u16 *)conp->vc_origin;
1074 static void fbcon_redraw(struct vc_data *conp, struct display *p,
1075 int line, int count, int offset)
1077 unsigned short *d = (unsigned short *)
1078 (conp->vc_origin + conp->vc_size_row * line);
1079 unsigned short *s = d + offset;
1081 while (count--) {
1082 unsigned short *start = s;
1083 unsigned short *le = advance_row(s, 1);
1084 unsigned short c;
1085 int x = 0;
1086 unsigned short attr = 1;
1088 do {
1089 c = scr_readw(s);
1090 if (attr != (c & 0xff00)) {
1091 attr = c & 0xff00;
1092 if (s > start) {
1093 p->dispsw->putcs(conp, p, start, s - start,
1094 real_y(p, line), x);
1095 x += s - start;
1096 start = s;
1099 if (c == scr_readw(d)) {
1100 if (s > start) {
1101 p->dispsw->putcs(conp, p, start, s - start,
1102 real_y(p, line), x);
1103 x += s - start + 1;
1104 start = s + 1;
1105 } else {
1106 x++;
1107 start++;
1110 scr_writew(c, d);
1111 s++;
1112 d++;
1113 } while (s < le);
1114 if (s > start)
1115 p->dispsw->putcs(conp, p, start, s - start, real_y(p, line), x);
1116 if (offset > 0)
1117 line++;
1118 else {
1119 line--;
1120 /* NOTE: We subtract two lines from these pointers */
1121 s -= conp->vc_size_row;
1122 d -= conp->vc_size_row;
1127 void fbcon_redraw_clear(struct vc_data *conp, struct display *p, int sy, int sx,
1128 int height, int width)
1130 int x, y;
1131 for (y=0; y<height; y++)
1132 for (x=0; x<width; x++)
1133 fbcon_putc(conp, ' ', sy+y, sx+x);
1136 /* This cannot be used together with ypan or ywrap */
1137 void fbcon_redraw_bmove(struct display *p, int sy, int sx, int dy, int dx, int h, int w)
1139 if (sy != dy)
1140 panic("fbcon_redraw_bmove width sy != dy");
1141 /* h will be always 1, but it does not matter if we are more generic */
1143 while (h-- > 0) {
1144 struct vc_data *conp = p->conp;
1145 unsigned short *d = (unsigned short *)
1146 (conp->vc_origin + conp->vc_size_row * dy + dx * 2);
1147 unsigned short *s = d + (dx - sx);
1148 unsigned short *start = d;
1149 unsigned short *ls = d;
1150 unsigned short *le = d + w;
1151 unsigned short c;
1152 int x = dx;
1153 unsigned short attr = 1;
1155 do {
1156 c = scr_readw(d);
1157 if (attr != (c & 0xff00)) {
1158 attr = c & 0xff00;
1159 if (d > start) {
1160 p->dispsw->putcs(conp, p, start, d - start, dy, x);
1161 x += d - start;
1162 start = d;
1165 if (s >= ls && s < le && c == scr_readw(s)) {
1166 if (d > start) {
1167 p->dispsw->putcs(conp, p, start, d - start, dy, x);
1168 x += d - start + 1;
1169 start = d + 1;
1170 } else {
1171 x++;
1172 start++;
1175 s++;
1176 d++;
1177 } while (d < le);
1178 if (d > start)
1179 p->dispsw->putcs(conp, p, start, d - start, dy, x);
1180 sy++;
1181 dy++;
1185 static inline void fbcon_softback_note(struct vc_data *conp, int t, int count)
1187 unsigned short *p;
1189 if (conp->vc_num != fg_console)
1190 return;
1191 p = (unsigned short *)(conp->vc_origin + t * conp->vc_size_row);
1193 while (count) {
1194 scr_memcpyw((u16 *)softback_in, p, conp->vc_size_row);
1195 count--;
1196 p = advance_row(p, 1);
1197 softback_in += conp->vc_size_row;
1198 if (softback_in == softback_end)
1199 softback_in = softback_buf;
1200 if (softback_in == softback_top) {
1201 softback_top += conp->vc_size_row;
1202 if (softback_top == softback_end)
1203 softback_top = softback_buf;
1206 softback_curr = softback_in;
1209 static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir,
1210 int count)
1212 int unit = conp->vc_num;
1213 struct display *p = &fb_display[unit];
1214 int scroll_partial = !(p->scrollmode & __SCROLL_YNOPARTIAL);
1216 if (!p->can_soft_blank && console_blanked)
1217 return 0;
1219 if (!count || vt_cons[unit]->vc_mode != KD_TEXT)
1220 return 0;
1222 fbcon_cursor(conp, CM_ERASE);
1225 * ++Geert: Only use ywrap/ypan if the console is in text mode
1226 * ++Andrew: Only use ypan on hardware text mode when scrolling the
1227 * whole screen (prevents flicker).
1230 switch (dir) {
1231 case SM_UP:
1232 if (count > conp->vc_rows) /* Maximum realistic size */
1233 count = conp->vc_rows;
1234 if (softback_top)
1235 fbcon_softback_note(conp, t, count);
1236 if (logo_shown >= 0) goto redraw_up;
1237 switch (p->scrollmode & __SCROLL_YMASK) {
1238 case __SCROLL_YMOVE:
1239 p->dispsw->bmove(p, t+count, 0, t, 0, b-t-count,
1240 conp->vc_cols);
1241 p->dispsw->clear(conp, p, b-count, 0, count,
1242 conp->vc_cols);
1243 break;
1245 case __SCROLL_YWRAP:
1246 if (b-t-count > 3*conp->vc_rows>>2) {
1247 if (t > 0)
1248 fbcon_bmove(conp, 0, 0, count, 0, t,
1249 conp->vc_cols);
1250 ywrap_up(unit, conp, p, count);
1251 if (conp->vc_rows-b > 0)
1252 fbcon_bmove(conp, b-count, 0, b, 0,
1253 conp->vc_rows-b, conp->vc_cols);
1254 } else if (p->scrollmode & __SCROLL_YPANREDRAW)
1255 goto redraw_up;
1256 else
1257 fbcon_bmove(conp, t+count, 0, t, 0, b-t-count,
1258 conp->vc_cols);
1259 fbcon_clear(conp, b-count, 0, count, conp->vc_cols);
1260 break;
1262 case __SCROLL_YPAN:
1263 if (( p->yscroll + count <= 2 * (p->vrows - conp->vc_rows)) &&
1264 (( !scroll_partial && (b-t == conp->vc_rows)) ||
1265 ( scroll_partial && (b-t-count > 3*conp->vc_rows>>2)))) {
1266 if (t > 0)
1267 fbcon_bmove(conp, 0, 0, count, 0, t,
1268 conp->vc_cols);
1269 ypan_up(unit, conp, p, count);
1270 if (conp->vc_rows-b > 0)
1271 fbcon_bmove(conp, b-count, 0, b, 0,
1272 conp->vc_rows-b, conp->vc_cols);
1273 } else if (p->scrollmode & __SCROLL_YPANREDRAW)
1274 goto redraw_up;
1275 else
1276 fbcon_bmove(conp, t+count, 0, t, 0, b-t-count,
1277 conp->vc_cols);
1278 fbcon_clear(conp, b-count, 0, count, conp->vc_cols);
1279 break;
1281 case __SCROLL_YREDRAW:
1282 redraw_up:
1283 fbcon_redraw(conp, p, t, b-t-count, count*conp->vc_cols);
1284 p->dispsw->clear(conp, p, real_y(p, b-count), 0,
1285 count, conp->vc_cols);
1286 scr_memsetw((unsigned short *)(conp->vc_origin +
1287 conp->vc_size_row * (b-count)),
1288 conp->vc_video_erase_char,
1289 conp->vc_size_row * count);
1290 return 1;
1292 break;
1294 case SM_DOWN:
1295 if (count > conp->vc_rows) /* Maximum realistic size */
1296 count = conp->vc_rows;
1297 switch (p->scrollmode & __SCROLL_YMASK) {
1298 case __SCROLL_YMOVE:
1299 p->dispsw->bmove(p, t, 0, t+count, 0, b-t-count,
1300 conp->vc_cols);
1301 p->dispsw->clear(conp, p, t, 0,
1302 count, conp->vc_cols);
1303 break;
1305 case __SCROLL_YWRAP:
1306 if (b-t-count > 3*conp->vc_rows>>2) {
1307 if (conp->vc_rows-b > 0)
1308 fbcon_bmove(conp, b, 0, b-count, 0,
1309 conp->vc_rows-b, conp->vc_cols);
1310 ywrap_down(unit, conp, p, count);
1311 if (t > 0)
1312 fbcon_bmove(conp, count, 0, 0, 0, t,
1313 conp->vc_cols);
1314 } else if (p->scrollmode & __SCROLL_YPANREDRAW)
1315 goto redraw_down;
1316 else
1317 fbcon_bmove(conp, t, 0, t+count, 0, b-t-count,
1318 conp->vc_cols);
1319 fbcon_clear(conp, t, 0, count, conp->vc_cols);
1320 break;
1322 case __SCROLL_YPAN:
1323 if (( count-p->yscroll <= p->vrows-conp->vc_rows) &&
1324 (( !scroll_partial && (b-t == conp->vc_rows)) ||
1325 ( scroll_partial && (b-t-count > 3*conp->vc_rows>>2)))) {
1326 if (conp->vc_rows-b > 0)
1327 fbcon_bmove(conp, b, 0, b-count, 0,
1328 conp->vc_rows-b, conp->vc_cols);
1329 ypan_down(unit, conp, p, count);
1330 if (t > 0)
1331 fbcon_bmove(conp, count, 0, 0, 0, t,
1332 conp->vc_cols);
1333 } else if (p->scrollmode & __SCROLL_YPANREDRAW)
1334 goto redraw_down;
1335 else
1336 fbcon_bmove(conp, t, 0, t+count, 0, b-t-count,
1337 conp->vc_cols);
1338 fbcon_clear(conp, t, 0, count, conp->vc_cols);
1339 break;
1341 case __SCROLL_YREDRAW:
1342 redraw_down:
1343 fbcon_redraw(conp, p, b - 1, b-t-count, -count*conp->vc_cols);
1344 p->dispsw->clear(conp, p, real_y(p, t), 0,
1345 count, conp->vc_cols);
1346 scr_memsetw((unsigned short *)(conp->vc_origin +
1347 conp->vc_size_row * t),
1348 conp->vc_video_erase_char,
1349 conp->vc_size_row * count);
1350 return 1;
1353 return 0;
1357 static void fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
1358 int height, int width)
1360 int unit = conp->vc_num;
1361 struct display *p = &fb_display[unit];
1363 if (!p->can_soft_blank && console_blanked)
1364 return;
1366 if (!width || !height)
1367 return;
1369 if (((sy <= p->cursor_y) && (p->cursor_y < sy+height) &&
1370 (sx <= p->cursor_x) && (p->cursor_x < sx+width)) ||
1371 ((dy <= p->cursor_y) && (p->cursor_y < dy+height) &&
1372 (dx <= p->cursor_x) && (p->cursor_x < dx+width)))
1373 fbcon_cursor(conp, CM_ERASE|CM_SOFTBACK);
1375 /* Split blits that cross physical y_wrap case.
1376 * Pathological case involves 4 blits, better to use recursive
1377 * code rather than unrolled case
1379 * Recursive invocations don't need to erase the cursor over and
1380 * over again, so we use fbcon_bmove_rec()
1382 fbcon_bmove_rec(p, sy, sx, dy, dx, height, width, p->vrows-p->yscroll);
1385 static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx,
1386 int height, int width, u_int y_break)
1388 u_int b;
1390 if (sy < y_break && sy+height > y_break) {
1391 b = y_break-sy;
1392 if (dy < sy) { /* Avoid trashing self */
1393 fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break);
1394 fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break);
1395 } else {
1396 fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break);
1397 fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break);
1399 return;
1402 if (dy < y_break && dy+height > y_break) {
1403 b = y_break-dy;
1404 if (dy < sy) { /* Avoid trashing self */
1405 fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break);
1406 fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break);
1407 } else {
1408 fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break);
1409 fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break);
1411 return;
1413 p->dispsw->bmove(p, real_y(p, sy), sx, real_y(p, dy), dx, height, width);
1417 static int fbcon_switch(struct vc_data *conp)
1419 int unit = conp->vc_num;
1420 struct display *p = &fb_display[unit];
1421 struct fb_info *info = p->fb_info;
1423 if (softback_top) {
1424 int l = fbcon_softback_size / conp->vc_size_row;
1425 if (softback_lines)
1426 fbcon_set_origin(conp);
1427 softback_top = softback_curr = softback_in = softback_buf;
1428 softback_lines = 0;
1430 if (l > 5)
1431 softback_end = softback_buf + l * conp->vc_size_row;
1432 else {
1433 /* Smaller scrollback makes no sense, and 0 would screw
1434 the operation totally */
1435 softback_top = 0;
1438 if (logo_shown >= 0) {
1439 struct vc_data *conp2 = vc_cons[logo_shown].d;
1441 if (conp2->vc_top == logo_lines && conp2->vc_bottom == conp2->vc_rows)
1442 conp2->vc_top = 0;
1443 logo_shown = -1;
1445 p->var.yoffset = p->yscroll = 0;
1446 switch (p->scrollmode & __SCROLL_YMASK) {
1447 case __SCROLL_YWRAP:
1448 scrollback_phys_max = p->vrows-conp->vc_rows;
1449 break;
1450 case __SCROLL_YPAN:
1451 scrollback_phys_max = p->vrows-2*conp->vc_rows;
1452 if (scrollback_phys_max < 0)
1453 scrollback_phys_max = 0;
1454 break;
1455 default:
1456 scrollback_phys_max = 0;
1457 break;
1459 scrollback_max = 0;
1460 scrollback_current = 0;
1462 if (info && info->switch_con)
1463 (*info->switch_con)(unit, info);
1464 if (p->dispsw->clear_margins && vt_cons[unit]->vc_mode == KD_TEXT)
1465 p->dispsw->clear_margins(conp, p, 0);
1466 if (logo_shown == -2) {
1467 logo_shown = fg_console;
1468 fbcon_show_logo(); /* This is protected above by initmem_freed */
1469 update_region(fg_console,
1470 conp->vc_origin + conp->vc_size_row * conp->vc_top,
1471 conp->vc_size_row * (conp->vc_bottom - conp->vc_top) / 2);
1472 return 0;
1474 return 1;
1478 static int fbcon_blank(struct vc_data *conp, int blank)
1480 struct display *p = &fb_display[conp->vc_num];
1481 struct fb_info *info = p->fb_info;
1483 if (blank < 0) /* Entering graphics mode */
1484 return 0;
1486 fbcon_cursor(p->conp, blank ? CM_ERASE : CM_DRAW);
1488 if (!p->can_soft_blank) {
1489 if (blank) {
1490 if (p->visual == FB_VISUAL_MONO01) {
1491 if (p->screen_base)
1492 fb_memset255(p->screen_base,
1493 p->var.xres_virtual*p->var.yres_virtual*
1494 p->var.bits_per_pixel>>3);
1495 } else {
1496 unsigned short oldc;
1497 u_int height;
1498 u_int y_break;
1500 oldc = conp->vc_video_erase_char;
1501 conp->vc_video_erase_char &= p->charmask;
1502 height = conp->vc_rows;
1503 y_break = p->vrows-p->yscroll;
1504 if (height > y_break) {
1505 p->dispsw->clear(conp, p, real_y(p, 0), 0, y_break, conp->vc_cols);
1506 p->dispsw->clear(conp, p, real_y(p, y_break), 0, height-y_break, conp->vc_cols);
1507 } else
1508 p->dispsw->clear(conp, p, real_y(p, 0), 0, height, conp->vc_cols);
1509 conp->vc_video_erase_char = oldc;
1511 return 0;
1512 } else {
1513 /* Tell console.c that it has to restore the screen itself */
1514 return 1;
1517 (*info->blank)(blank, info);
1518 return 0;
1521 static void fbcon_free_font(struct display *p)
1523 if (p->userfont && p->fontdata &&
1524 (--REFCOUNT(p->fontdata) == 0))
1525 kfree(p->fontdata - FONT_EXTRA_WORDS*sizeof(int));
1526 p->fontdata = NULL;
1527 p->userfont = 0;
1530 static inline int fbcon_get_font(int unit, struct console_font_op *op)
1532 struct display *p = &fb_display[unit];
1533 u8 *data = op->data;
1534 u8 *fontdata = p->fontdata;
1535 int i, j;
1537 #ifdef CONFIG_FBCON_FONTWIDTH8_ONLY
1538 if (fontwidth(p) != 8) return -EINVAL;
1539 #endif
1540 op->width = fontwidth(p);
1541 op->height = fontheight(p);
1542 op->charcount = (p->charmask == 0x1ff) ? 512 : 256;
1543 if (!op->data) return 0;
1545 if (op->width <= 8) {
1546 j = fontheight(p);
1547 for (i = 0; i < op->charcount; i++) {
1548 memcpy(data, fontdata, j);
1549 memset(data+j, 0, 32-j);
1550 data += 32;
1551 fontdata += j;
1554 #ifndef CONFIG_FBCON_FONTWIDTH8_ONLY
1555 else if (op->width <= 16) {
1556 j = fontheight(p) * 2;
1557 for (i = 0; i < op->charcount; i++) {
1558 memcpy(data, fontdata, j);
1559 memset(data+j, 0, 64-j);
1560 data += 64;
1561 fontdata += j;
1563 } else if (op->width <= 24) {
1564 for (i = 0; i < op->charcount; i++) {
1565 for (j = 0; j < fontheight(p); j++) {
1566 *data++ = fontdata[0];
1567 *data++ = fontdata[1];
1568 *data++ = fontdata[2];
1569 fontdata += sizeof(u32);
1571 memset(data, 0, 3*(32-j));
1572 data += 3 * (32 - j);
1574 } else {
1575 j = fontheight(p) * 4;
1576 for (i = 0; i < op->charcount; i++) {
1577 memcpy(data, fontdata, j);
1578 memset(data+j, 0, 128-j);
1579 data += 128;
1580 fontdata += j;
1583 #endif
1584 return 0;
1587 static int fbcon_do_set_font(int unit, struct console_font_op *op, u8 *data, int userfont)
1589 struct display *p = &fb_display[unit];
1590 int resize;
1591 int w = op->width;
1592 int h = op->height;
1593 int cnt;
1594 char *old_data = NULL;
1596 if (!fontwidthvalid(p,w)) {
1597 if (userfont && op->op != KD_FONT_OP_COPY)
1598 kfree(data - FONT_EXTRA_WORDS*sizeof(int));
1599 return -ENXIO;
1602 if (CON_IS_VISIBLE(p->conp) && softback_lines)
1603 fbcon_set_origin(p->conp);
1605 resize = (w != fontwidth(p)) || (h != fontheight(p));
1606 if (p->userfont)
1607 old_data = p->fontdata;
1608 if (userfont)
1609 cnt = FNTCHARCNT(data);
1610 else
1611 cnt = 256;
1612 p->fontdata = data;
1613 if ((p->userfont = userfont))
1614 REFCOUNT(data)++;
1615 p->_fontwidth = w;
1616 p->_fontheight = h;
1617 if (p->conp->vc_hi_font_mask && cnt == 256) {
1618 p->conp->vc_hi_font_mask = 0;
1619 if (p->conp->vc_can_do_color)
1620 p->conp->vc_complement_mask >>= 1;
1621 p->fgshift--;
1622 p->bgshift--;
1623 p->charmask = 0xff;
1625 /* ++Edmund: reorder the attribute bits */
1626 if (p->conp->vc_can_do_color) {
1627 struct vc_data *conp = p->conp;
1628 unsigned short *cp = (unsigned short *) conp->vc_origin;
1629 int count = conp->vc_screenbuf_size/2;
1630 unsigned short c;
1631 for (; count > 0; count--, cp++) {
1632 c = scr_readw(cp);
1633 scr_writew(((c & 0xfe00) >> 1) | (c & 0xff), cp);
1635 c = conp->vc_video_erase_char;
1636 conp->vc_video_erase_char = ((c & 0xfe00) >> 1) | (c & 0xff);
1637 conp->vc_attr >>= 1;
1640 } else if (!p->conp->vc_hi_font_mask && cnt == 512) {
1641 p->conp->vc_hi_font_mask = 0x100;
1642 if (p->conp->vc_can_do_color)
1643 p->conp->vc_complement_mask <<= 1;
1644 p->fgshift++;
1645 p->bgshift++;
1646 p->charmask = 0x1ff;
1648 /* ++Edmund: reorder the attribute bits */
1650 struct vc_data *conp = p->conp;
1651 unsigned short *cp = (unsigned short *) conp->vc_origin;
1652 int count = conp->vc_screenbuf_size/2;
1653 unsigned short c;
1654 for (; count > 0; count--, cp++) {
1655 unsigned short newc;
1656 c = scr_readw(cp);
1657 if (conp->vc_can_do_color)
1658 newc = ((c & 0xff00) << 1) | (c & 0xff);
1659 else
1660 newc = c & ~0x100;
1661 scr_writew(newc, cp);
1663 c = conp->vc_video_erase_char;
1664 if (conp->vc_can_do_color) {
1665 conp->vc_video_erase_char = ((c & 0xff00) << 1) | (c & 0xff);
1666 conp->vc_attr <<= 1;
1667 } else
1668 conp->vc_video_erase_char = c & ~0x100;
1672 fbcon_font_widths(p);
1674 if (resize) {
1675 struct vc_data *conp = p->conp;
1676 /* reset wrap/pan */
1677 p->var.xoffset = p->var.yoffset = p->yscroll = 0;
1678 p->vrows = p->var.yres_virtual/h;
1679 if ((p->var.yres % h) && (p->var.yres_virtual % h < p->var.yres % h))
1680 p->vrows--;
1681 updatescrollmode(p);
1682 vc_resize_con( p->var.yres/h, p->var.xres/w, unit );
1683 if (CON_IS_VISIBLE(conp) && softback_buf) {
1684 int l = fbcon_softback_size / conp->vc_size_row;
1685 if (l > 5)
1686 softback_end = softback_buf + l * conp->vc_size_row;
1687 else {
1688 /* Smaller scrollback makes no sense, and 0 would screw
1689 the operation totally */
1690 softback_top = 0;
1693 } else if (CON_IS_VISIBLE(p->conp) && vt_cons[unit]->vc_mode == KD_TEXT) {
1694 if (p->dispsw->clear_margins)
1695 p->dispsw->clear_margins(p->conp, p, 0);
1696 update_screen(unit);
1699 if (old_data && (--REFCOUNT(old_data) == 0))
1700 kfree(old_data - FONT_EXTRA_WORDS*sizeof(int));
1702 return 0;
1705 static inline int fbcon_copy_font(int unit, struct console_font_op *op)
1707 struct display *od, *p = &fb_display[unit];
1708 int h = op->height;
1710 if (h < 0 || !vc_cons_allocated( h ))
1711 return -ENOTTY;
1712 if (h == unit)
1713 return 0; /* nothing to do */
1714 od = &fb_display[h];
1715 if (od->fontdata == p->fontdata)
1716 return 0; /* already the same font... */
1717 op->width = fontwidth(od);
1718 op->height = fontheight(od);
1719 return fbcon_do_set_font(unit, op, od->fontdata, od->userfont);
1722 static inline int fbcon_set_font(int unit, struct console_font_op *op)
1724 int w = op->width;
1725 int h = op->height;
1726 int size = h;
1727 int i, k;
1728 u8 *new_data, *data = op->data, *p;
1730 #ifdef CONFIG_FBCON_FONTWIDTH8_ONLY
1731 if (w != 8)
1732 return -EINVAL;
1733 #endif
1734 if ((w <= 0) || (w > 32) || (op->charcount != 256 && op->charcount != 512))
1735 return -EINVAL;
1737 if (w > 8) {
1738 if (w <= 16)
1739 size *= 2;
1740 else
1741 size *= 4;
1743 size *= op->charcount;
1745 if (!(new_data = kmalloc(FONT_EXTRA_WORDS*sizeof(int)+size, GFP_USER)))
1746 return -ENOMEM;
1747 new_data += FONT_EXTRA_WORDS*sizeof(int);
1748 FNTSIZE(new_data) = size;
1749 FNTCHARCNT(new_data) = op->charcount;
1750 REFCOUNT(new_data) = 0; /* usage counter */
1751 p = new_data;
1752 if (w <= 8) {
1753 for (i = 0; i < op->charcount; i++) {
1754 memcpy(p, data, h);
1755 data += 32;
1756 p += h;
1759 #ifndef CONFIG_FBCON_FONTWIDTH8_ONLY
1760 else if (w <= 16) {
1761 h *= 2;
1762 for (i = 0; i < op->charcount; i++) {
1763 memcpy(p, data, h);
1764 data += 64;
1765 p += h;
1767 } else if (w <= 24) {
1768 for (i = 0; i < op->charcount; i++) {
1769 int j;
1770 for (j = 0; j < h; j++) {
1771 memcpy(p, data, 3);
1772 p[3] = 0;
1773 data += 3;
1774 p += sizeof(u32);
1776 data += 3*(32 - h);
1778 } else {
1779 h *= 4;
1780 for (i = 0; i < op->charcount; i++) {
1781 memcpy(p, data, h);
1782 data += 128;
1783 p += h;
1786 #endif
1787 /* we can do it in u32 chunks because of charcount is 256 or 512, so
1788 font length must be multiple of 256, at least. And 256 is multiple
1789 of 4 */
1790 k = 0;
1791 while (p > new_data) k += *--(u32 *)p;
1792 FNTSUM(new_data) = k;
1793 /* Check if the same font is on some other console already */
1794 for (i = 0; i < MAX_NR_CONSOLES; i++) {
1795 if (fb_display[i].userfont &&
1796 fb_display[i].fontdata &&
1797 FNTSUM(fb_display[i].fontdata) == k &&
1798 FNTSIZE(fb_display[i].fontdata) == size &&
1799 fontwidth(&fb_display[i]) == w &&
1800 !memcmp(fb_display[i].fontdata, new_data, size)) {
1801 kfree(new_data - FONT_EXTRA_WORDS*sizeof(int));
1802 new_data = fb_display[i].fontdata;
1803 break;
1806 return fbcon_do_set_font(unit, op, new_data, 1);
1809 static inline int fbcon_set_def_font(int unit, struct console_font_op *op)
1811 char name[MAX_FONT_NAME];
1812 struct fbcon_font_desc *f;
1813 struct display *p = &fb_display[unit];
1815 if (!op->data)
1816 f = fbcon_get_default_font(p->var.xres, p->var.yres);
1817 else if (strncpy_from_user(name, op->data, MAX_FONT_NAME-1) < 0)
1818 return -EFAULT;
1819 else {
1820 name[MAX_FONT_NAME-1] = 0;
1821 if (!(f = fbcon_find_font(name)))
1822 return -ENOENT;
1824 op->width = f->width;
1825 op->height = f->height;
1826 return fbcon_do_set_font(unit, op, f->data, 0);
1829 static int fbcon_font_op(struct vc_data *conp, struct console_font_op *op)
1831 int unit = conp->vc_num;
1833 switch (op->op) {
1834 case KD_FONT_OP_SET:
1835 return fbcon_set_font(unit, op);
1836 case KD_FONT_OP_GET:
1837 return fbcon_get_font(unit, op);
1838 case KD_FONT_OP_SET_DEFAULT:
1839 return fbcon_set_def_font(unit, op);
1840 case KD_FONT_OP_COPY:
1841 return fbcon_copy_font(unit, op);
1842 default:
1843 return -ENOSYS;
1847 static u16 palette_red[16];
1848 static u16 palette_green[16];
1849 static u16 palette_blue[16];
1851 static struct fb_cmap palette_cmap = {
1852 0, 16, palette_red, palette_green, palette_blue, NULL
1855 static int fbcon_set_palette(struct vc_data *conp, unsigned char *table)
1857 int unit = conp->vc_num;
1858 struct display *p = &fb_display[unit];
1859 int i, j, k;
1860 u8 val;
1862 if (!conp->vc_can_do_color || (!p->can_soft_blank && console_blanked))
1863 return -EINVAL;
1864 for (i = j = 0; i < 16; i++) {
1865 k = table[i];
1866 val = conp->vc_palette[j++];
1867 palette_red[k] = (val<<8)|val;
1868 val = conp->vc_palette[j++];
1869 palette_green[k] = (val<<8)|val;
1870 val = conp->vc_palette[j++];
1871 palette_blue[k] = (val<<8)|val;
1873 if (p->var.bits_per_pixel <= 4)
1874 palette_cmap.len = 1<<p->var.bits_per_pixel;
1875 else
1876 palette_cmap.len = 16;
1877 palette_cmap.start = 0;
1878 return p->fb_info->fbops->fb_set_cmap(&palette_cmap, 1, unit, p->fb_info);
1881 static u16 *fbcon_screen_pos(struct vc_data *conp, int offset)
1883 int line;
1884 unsigned long p;
1886 if (conp->vc_num != fg_console || !softback_lines)
1887 return (u16 *)(conp->vc_origin + offset);
1888 line = offset / conp->vc_size_row;
1889 if (line >= softback_lines)
1890 return (u16 *)(conp->vc_origin + offset - softback_lines * conp->vc_size_row);
1891 p = softback_curr + offset;
1892 if (p >= softback_end)
1893 p += softback_buf - softback_end;
1894 return (u16 *)p;
1897 static unsigned long fbcon_getxy(struct vc_data *conp, unsigned long pos, int *px, int *py)
1899 int x, y;
1900 unsigned long ret;
1901 if (pos >= conp->vc_origin && pos < conp->vc_scr_end) {
1902 unsigned long offset = (pos - conp->vc_origin) / 2;
1904 x = offset % conp->vc_cols;
1905 y = offset / conp->vc_cols;
1906 if (conp->vc_num == fg_console)
1907 y += softback_lines;
1908 ret = pos + (conp->vc_cols - x) * 2;
1909 } else if (conp->vc_num == fg_console && softback_lines) {
1910 unsigned long offset = (pos - softback_curr) / 2;
1912 x = offset % conp->vc_cols;
1913 y = offset / conp->vc_cols;
1914 if (pos < softback_curr)
1915 y += (softback_end - softback_buf) / conp->vc_size_row;
1916 ret = pos + (conp->vc_cols - x) * 2;
1917 if (ret == softback_end)
1918 ret = softback_buf;
1919 if (ret == softback_in)
1920 ret = conp->vc_origin;
1921 } else {
1922 /* Should not happen */
1923 x = y = 0;
1924 ret = conp->vc_origin;
1926 if (px) *px = x;
1927 if (py) *py = y;
1928 return ret;
1931 /* As we might be inside of softback, we may work with non-contiguous buffer,
1932 that's why we have to use a separate routine. */
1933 static void fbcon_invert_region(struct vc_data *conp, u16 *p, int cnt)
1935 while (cnt--) {
1936 if (!conp->vc_can_do_color)
1937 *p++ ^= 0x0800;
1938 else if (conp->vc_hi_font_mask == 0x100) {
1939 u16 a = *p;
1940 a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) | (((a) & 0x0e00) << 4);
1941 *p++ = a;
1942 } else {
1943 u16 a = *p;
1944 a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4);
1945 *p++ = a;
1947 if (p == (u16 *)softback_end)
1948 p = (u16 *)softback_buf;
1949 if (p == (u16 *)softback_in)
1950 p = (u16 *)conp->vc_origin;
1954 static int fbcon_scrolldelta(struct vc_data *conp, int lines)
1956 int unit, offset, limit, scrollback_old;
1957 struct display *p;
1959 unit = fg_console;
1960 p = &fb_display[unit];
1961 if (softback_top) {
1962 if (conp->vc_num != unit)
1963 return 0;
1964 if (vt_cons[unit]->vc_mode != KD_TEXT || !lines)
1965 return 0;
1966 if (logo_shown >= 0) {
1967 struct vc_data *conp2 = vc_cons[logo_shown].d;
1969 if (conp2->vc_top == logo_lines && conp2->vc_bottom == conp2->vc_rows)
1970 conp2->vc_top = 0;
1971 if (logo_shown == unit) {
1972 unsigned long p, q;
1973 int i;
1975 p = softback_in;
1976 q = conp->vc_origin + logo_lines * conp->vc_size_row;
1977 for (i = 0; i < logo_lines; i++) {
1978 if (p == softback_top) break;
1979 if (p == softback_buf) p = softback_end;
1980 p -= conp->vc_size_row;
1981 q -= conp->vc_size_row;
1982 scr_memcpyw((u16 *)q, (u16 *)p, conp->vc_size_row);
1984 softback_in = p;
1985 update_region(unit, conp->vc_origin, logo_lines * conp->vc_cols);
1987 logo_shown = -1;
1989 fbcon_cursor(conp, CM_ERASE|CM_SOFTBACK);
1990 fbcon_redraw_softback(conp, p, lines);
1991 fbcon_cursor(conp, CM_DRAW|CM_SOFTBACK);
1992 return 0;
1995 if (!scrollback_phys_max)
1996 return -ENOSYS;
1998 scrollback_old = scrollback_current;
1999 scrollback_current -= lines;
2000 if (scrollback_current < 0)
2001 scrollback_current = 0;
2002 else if (scrollback_current > scrollback_max)
2003 scrollback_current = scrollback_max;
2004 if (scrollback_current == scrollback_old)
2005 return 0;
2007 if (!p->can_soft_blank &&
2008 (console_blanked || vt_cons[unit]->vc_mode != KD_TEXT || !lines))
2009 return 0;
2010 fbcon_cursor(conp, CM_ERASE);
2012 offset = p->yscroll-scrollback_current;
2013 limit = p->vrows;
2014 switch (p->scrollmode && __SCROLL_YMASK) {
2015 case __SCROLL_YWRAP:
2016 p->var.vmode |= FB_VMODE_YWRAP;
2017 break;
2018 case __SCROLL_YPAN:
2019 limit -= conp->vc_rows;
2020 p->var.vmode &= ~FB_VMODE_YWRAP;
2021 break;
2023 if (offset < 0)
2024 offset += limit;
2025 else if (offset >= limit)
2026 offset -= limit;
2027 p->var.xoffset = 0;
2028 p->var.yoffset = offset*fontheight(p);
2029 p->fb_info->updatevar(unit, p->fb_info);
2030 if (!scrollback_current)
2031 fbcon_cursor(conp, CM_DRAW);
2032 return 0;
2035 static int fbcon_set_origin(struct vc_data *conp)
2037 if (softback_lines && !console_blanked)
2038 fbcon_scrolldelta(conp, softback_lines);
2039 return 0;
2042 static inline unsigned safe_shift(unsigned d,int n)
2044 return n<0 ? d>>-n : d<<n;
2047 static int __init fbcon_show_logo( void )
2049 struct display *p = &fb_display[fg_console]; /* draw to vt in foreground */
2050 int depth = p->var.bits_per_pixel;
2051 int line = p->next_line;
2052 unsigned char *fb = p->screen_base;
2053 unsigned char *logo;
2054 unsigned char *dst, *src;
2055 int i, j, n, x1, y1, x;
2056 int logo_depth, done = 0;
2058 /* Return if the frame buffer is not mapped */
2059 if (!fb)
2060 return 0;
2062 /* Set colors if visual is PSEUDOCOLOR and we have enough colors, or for
2063 * DIRECTCOLOR */
2064 if ((p->visual == FB_VISUAL_PSEUDOCOLOR && depth >= 4) ||
2065 p->visual == FB_VISUAL_DIRECTCOLOR) {
2066 int is_truecolor = (p->visual == FB_VISUAL_DIRECTCOLOR);
2067 int use_256 = (!is_truecolor && depth >= 8) ||
2068 (is_truecolor && depth >= 24);
2069 int first_col = use_256 ? 32 : depth > 4 ? 16 : 0;
2070 int num_cols = use_256 ? LINUX_LOGO_COLORS : 16;
2071 unsigned char *red, *green, *blue;
2073 if (use_256) {
2074 red = linux_logo_red;
2075 green = linux_logo_green;
2076 blue = linux_logo_blue;
2078 else {
2079 red = linux_logo16_red;
2080 green = linux_logo16_green;
2081 blue = linux_logo16_blue;
2084 for( i = 0; i < num_cols; i += n ) {
2085 n = num_cols - i;
2086 if (n > 16)
2087 /* palette_cmap provides space for only 16 colors at once */
2088 n = 16;
2089 palette_cmap.start = first_col + i;
2090 palette_cmap.len = n;
2091 for( j = 0; j < n; ++j ) {
2092 palette_cmap.red[j] = (red[i+j] << 8) | red[i+j];
2093 palette_cmap.green[j] = (green[i+j] << 8) | green[i+j];
2094 palette_cmap.blue[j] = (blue[i+j] << 8) | blue[i+j];
2096 p->fb_info->fbops->fb_set_cmap(&palette_cmap, 1, fg_console,
2097 p->fb_info);
2101 if (depth >= 8) {
2102 logo = linux_logo;
2103 logo_depth = 8;
2105 else if (depth >= 4) {
2106 logo = linux_logo16;
2107 logo_depth = 4;
2109 else {
2110 logo = linux_logo_bw;
2111 logo_depth = 1;
2114 if (p->fb_info->fbops->fb_rasterimg)
2115 p->fb_info->fbops->fb_rasterimg(p->fb_info, 1);
2117 for (x = 0; x < smp_num_cpus * (LOGO_W + 8) &&
2118 x < p->var.xres - (LOGO_W + 8); x += (LOGO_W + 8)) {
2120 #if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CFB24) || \
2121 defined(CONFIG_FBCON_CFB32) || defined(CONFIG_FB_SBUS)
2122 if (p->visual == FB_VISUAL_DIRECTCOLOR) {
2123 unsigned int val; /* max. depth 32! */
2124 int bdepth;
2125 int redshift, greenshift, blueshift;
2127 /* Bug: Doesn't obey msb_right ... (who needs that?) */
2128 redshift = p->var.red.offset;
2129 greenshift = p->var.green.offset;
2130 blueshift = p->var.blue.offset;
2132 if (depth >= 24 && (depth % 8) == 0) {
2133 /* have at least 8 bits per color */
2134 src = logo;
2135 bdepth = depth/8;
2136 for( y1 = 0; y1 < LOGO_H; y1++ ) {
2137 dst = fb + y1*line + x*bdepth;
2138 for( x1 = 0; x1 < LOGO_W; x1++, src++ ) {
2139 val = (*src << redshift) |
2140 (*src << greenshift) |
2141 (*src << blueshift);
2142 if (bdepth == 4 && !((long)dst & 3)) {
2143 /* Some cards require 32bit access */
2144 fb_writel (val, dst);
2145 dst += 4;
2146 } else {
2147 #ifdef __LITTLE_ENDIAN
2148 for( i = 0; i < bdepth; ++i )
2149 #else
2150 for( i = bdepth-1; i >= 0; --i )
2151 #endif
2152 fb_writeb (val >> (i*8), dst++);
2157 else if (depth >= 15 && depth <= 23) {
2158 /* have 5..7 bits per color, using 16 color image */
2159 unsigned int pix;
2160 src = linux_logo16;
2161 bdepth = (depth+7)/8;
2162 for( y1 = 0; y1 < LOGO_H; y1++ ) {
2163 dst = fb + y1*line + x*bdepth;
2164 for( x1 = 0; x1 < LOGO_W/2; x1++, src++ ) {
2165 pix = (*src >> 4) | 0x10; /* upper nibble */
2166 val = (pix << redshift) |
2167 (pix << greenshift) |
2168 (pix << blueshift);
2169 #ifdef __LITTLE_ENDIAN
2170 for( i = 0; i < bdepth; ++i )
2171 #else
2172 for( i = bdepth-1; i >= 0; --i )
2173 #endif
2174 fb_writeb (val >> (i*8), dst++);
2175 pix = (*src & 0x0f) | 0x10; /* lower nibble */
2176 val = (pix << redshift) |
2177 (pix << greenshift) |
2178 (pix << blueshift);
2179 #ifdef __LITTLE_ENDIAN
2180 for( i = 0; i < bdepth; ++i )
2181 #else
2182 for( i = bdepth-1; i >= 0; --i )
2183 #endif
2184 fb_writeb (val >> (i*8), dst++);
2188 done = 1;
2190 #endif
2191 #if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CFB24) || \
2192 defined(CONFIG_FBCON_CFB32) || defined(CONFIG_FB_SBUS)
2193 if ((depth % 8 == 0) && (p->visual == FB_VISUAL_TRUECOLOR)) {
2194 /* Modes without color mapping, needs special data transformation... */
2195 unsigned int val; /* max. depth 32! */
2196 int bdepth = depth/8;
2197 unsigned char mask[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
2198 unsigned char redmask, greenmask, bluemask;
2199 int redshift, greenshift, blueshift;
2201 /* Bug: Doesn't obey msb_right ... (who needs that?) */
2202 redmask = mask[p->var.red.length < 8 ? p->var.red.length : 8];
2203 greenmask = mask[p->var.green.length < 8 ? p->var.green.length : 8];
2204 bluemask = mask[p->var.blue.length < 8 ? p->var.blue.length : 8];
2205 redshift = p->var.red.offset - (8-p->var.red.length);
2206 greenshift = p->var.green.offset - (8-p->var.green.length);
2207 blueshift = p->var.blue.offset - (8-p->var.blue.length);
2209 src = logo;
2210 for( y1 = 0; y1 < LOGO_H; y1++ ) {
2211 dst = fb + y1*line + x*bdepth;
2212 for( x1 = 0; x1 < LOGO_W; x1++, src++ ) {
2213 val = safe_shift((linux_logo_red[*src-32] & redmask), redshift) |
2214 safe_shift((linux_logo_green[*src-32] & greenmask), greenshift) |
2215 safe_shift((linux_logo_blue[*src-32] & bluemask), blueshift);
2216 if (bdepth == 4 && !((long)dst & 3)) {
2217 /* Some cards require 32bit access */
2218 fb_writel (val, dst);
2219 dst += 4;
2220 } else {
2221 #ifdef __LITTLE_ENDIAN
2222 for( i = 0; i < bdepth; ++i )
2223 #else
2224 for( i = bdepth-1; i >= 0; --i )
2225 #endif
2226 fb_writeb (val >> (i*8), dst++);
2230 done = 1;
2232 #endif
2233 #if defined(CONFIG_FBCON_CFB4)
2234 if (depth == 4 && p->type == FB_TYPE_PACKED_PIXELS) {
2235 src = logo;
2236 for( y1 = 0; y1 < LOGO_H; y1++) {
2237 dst = fb + y1*line + x/2;
2238 for( x1 = 0; x1 < LOGO_W/2; x1++) {
2239 u8 q = *src++;
2240 q = (q << 4) | (q >> 4);
2241 fb_writeb (q, dst++);
2244 done = 1;
2246 #endif
2247 #if defined(CONFIG_FBCON_CFB8) || defined(CONFIG_FB_SBUS)
2248 if (depth == 8 && p->type == FB_TYPE_PACKED_PIXELS) {
2249 /* depth 8 or more, packed, with color registers */
2251 src = logo;
2252 for( y1 = 0; y1 < LOGO_H; y1++ ) {
2253 dst = fb + y1*line + x;
2254 for( x1 = 0; x1 < LOGO_W; x1++ )
2255 fb_writeb (*src++, dst++);
2257 done = 1;
2259 #endif
2260 #if defined(CONFIG_FBCON_AFB) || defined(CONFIG_FBCON_ILBM) || \
2261 defined(CONFIG_FBCON_IPLAN2P2) || defined(CONFIG_FBCON_IPLAN2P4) || \
2262 defined(CONFIG_FBCON_IPLAN2P8)
2263 if (depth >= 2 && (p->type == FB_TYPE_PLANES ||
2264 p->type == FB_TYPE_INTERLEAVED_PLANES)) {
2265 /* planes (normal or interleaved), with color registers */
2266 int bit;
2267 unsigned char val, mask;
2268 int plane = p->next_plane;
2270 #if defined(CONFIG_FBCON_IPLAN2P2) || defined(CONFIG_FBCON_IPLAN2P4) || \
2271 defined(CONFIG_FBCON_IPLAN2P8)
2272 int line_length = p->line_length;
2274 /* for support of Atari interleaved planes */
2275 #define MAP_X(x) (line_length ? (x) : ((x) & ~1)*depth + ((x) & 1))
2276 #else
2277 #define MAP_X(x) (x)
2278 #endif
2279 /* extract a bit from the source image */
2280 #define BIT(p,pix,bit) (p[pix*logo_depth/8] & \
2281 (1 << ((8-((pix*logo_depth)&7)-logo_depth) + bit)))
2283 src = logo;
2284 for( y1 = 0; y1 < LOGO_H; y1++ ) {
2285 for( x1 = 0; x1 < LOGO_LINE; x1++, src += logo_depth ) {
2286 dst = fb + y1*line + MAP_X(x/8+x1);
2287 for( bit = 0; bit < logo_depth; bit++ ) {
2288 val = 0;
2289 for( mask = 0x80, i = 0; i < 8; mask >>= 1, i++ ) {
2290 if (BIT( src, i, bit ))
2291 val |= mask;
2293 *dst = val;
2294 dst += plane;
2299 /* fill remaining planes
2300 * special case for logo_depth == 4: we used color registers 16..31,
2301 * so fill plane 4 with 1 bits instead of 0 */
2302 if (depth > logo_depth) {
2303 for( y1 = 0; y1 < LOGO_H; y1++ ) {
2304 for( x1 = 0; x1 < LOGO_LINE; x1++ ) {
2305 dst = fb + y1*line + MAP_X(x/8+x1) + logo_depth*plane;
2306 for( i = logo_depth; i < depth; i++, dst += plane )
2307 *dst = (i == logo_depth && logo_depth == 4)
2308 ? 0xff : 0x00;
2312 done = 1;
2313 break;
2315 #endif
2316 #if defined(CONFIG_FBCON_MFB) || defined(CONFIG_FBCON_AFB) || \
2317 defined(CONFIG_FBCON_ILBM) || defined(CONFIG_FBCON_HGA)
2319 if (depth == 1 && (p->type == FB_TYPE_PACKED_PIXELS ||
2320 p->type == FB_TYPE_PLANES ||
2321 p->type == FB_TYPE_INTERLEAVED_PLANES)) {
2323 /* monochrome */
2324 unsigned char inverse = p->inverse || p->visual == FB_VISUAL_MONO01
2325 ? 0x00 : 0xff;
2327 int is_hga = !strncmp(p->fb_info->modename, "HGA", 3);
2328 /* can't use simply memcpy because need to apply inverse */
2329 for( y1 = 0; y1 < LOGO_H; y1++ ) {
2330 src = logo + y1*LOGO_LINE;
2331 if (is_hga)
2332 dst = fb + (y1%4)*8192 + (y1>>2)*line + x/8;
2333 else
2334 dst = fb + y1*line + x/8;
2335 for( x1 = 0; x1 < LOGO_LINE; ++x1 )
2336 fb_writeb(fb_readb(src++) ^ inverse, dst++);
2338 done = 1;
2340 #endif
2341 #if defined(CONFIG_FBCON_VGA_PLANES)
2342 if (depth == 4 && p->type == FB_TYPE_VGA_PLANES) {
2343 outb_p(1,0x3ce); outb_p(0xf,0x3cf);
2344 outb_p(3,0x3ce); outb_p(0,0x3cf);
2345 outb_p(5,0x3ce); outb_p(0,0x3cf);
2347 src = logo;
2348 for (y1 = 0; y1 < LOGO_H; y1++) {
2349 for (x1 = 0; x1 < LOGO_W / 2; x1++) {
2350 dst = fb + y1*line + x1/4 + x/8;
2352 outb_p(0,0x3ce);
2353 outb_p(*src >> 4,0x3cf);
2354 outb_p(8,0x3ce);
2355 outb_p(1 << (7 - x1 % 4 * 2),0x3cf);
2356 fb_readb (dst);
2357 fb_writeb (0, dst);
2359 outb_p(0,0x3ce);
2360 outb_p(*src & 0xf,0x3cf);
2361 outb_p(8,0x3ce);
2362 outb_p(1 << (7 - (1 + x1 % 4 * 2)),0x3cf);
2363 fb_readb (dst);
2364 fb_writeb (0, dst);
2366 src++;
2369 done = 1;
2371 #endif
2374 if (p->fb_info->fbops->fb_rasterimg)
2375 p->fb_info->fbops->fb_rasterimg(p->fb_info, 0);
2377 /* Modes not yet supported: packed pixels with depth != 8 (does such a
2378 * thing exist in reality?) */
2380 return done ? (LOGO_H + fontheight(p) - 1) / fontheight(p) : 0 ;
2384 * The console `switch' structure for the frame buffer based console
2387 struct consw fb_con = {
2388 con_startup: fbcon_startup,
2389 con_init: fbcon_init,
2390 con_deinit: fbcon_deinit,
2391 con_clear: fbcon_clear,
2392 con_putc: fbcon_putc,
2393 con_putcs: fbcon_putcs,
2394 con_cursor: fbcon_cursor,
2395 con_scroll: fbcon_scroll,
2396 con_bmove: fbcon_bmove,
2397 con_switch: fbcon_switch,
2398 con_blank: fbcon_blank,
2399 con_font_op: fbcon_font_op,
2400 con_set_palette: fbcon_set_palette,
2401 con_scrolldelta: fbcon_scrolldelta,
2402 con_set_origin: fbcon_set_origin,
2403 con_invert_region: fbcon_invert_region,
2404 con_screen_pos: fbcon_screen_pos,
2405 con_getxy: fbcon_getxy,
2410 * Dummy Low Level Operations
2413 static void fbcon_dummy_op(void) {}
2415 #define DUMMY (void *)fbcon_dummy_op
2417 struct display_switch fbcon_dummy = {
2418 setup: DUMMY,
2419 bmove: DUMMY,
2420 clear: DUMMY,
2421 putc: DUMMY,
2422 putcs: DUMMY,
2423 revc: DUMMY,
2428 * Visible symbols for modules
2431 EXPORT_SYMBOL(fb_display);
2432 EXPORT_SYMBOL(fbcon_redraw_bmove);
2433 EXPORT_SYMBOL(fbcon_redraw_clear);
2434 EXPORT_SYMBOL(fbcon_dummy);
2435 EXPORT_SYMBOL(fb_con);