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
11 * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk]
13 * with work by William Rucklidge (wjr@cs.cornell.edu)
15 * Jes Sorensen (jds@kom.auc.dk)
18 * and on the original Atari console driver (atacon.c):
20 * Copyright (C) 1993 Bjoern Brauel
23 * with work by Guenther Kelleter
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>
34 * 2001 - Documented with DocBook
35 * - Brad Douglas <brad@neruo.com>
37 * The low level operations for the various display memory organizations are
38 * now in separate source files.
40 * Currently the following organizations are supported:
42 * o afb Amiga bitplanes
43 * o cfb{2,4,8,16,24,32} Packed pixels
44 * o ilbm Amiga interleaved bitplanes
45 * o iplan2p[248] Atari interleaved bitplanes
47 * o vga VGA characters/attributes
51 * - Implement 16 plane mode (iplan2p16)
54 * This file is subject to the terms and conditions of the GNU General Public
55 * License. See the file COPYING in the main directory of this archive for
61 #include <linux/config.h>
62 #include <linux/module.h>
63 #include <linux/types.h>
64 #include <linux/sched.h>
66 #include <linux/kernel.h>
67 #include <linux/delay.h> /* MSch: for IRQ probe */
68 #include <linux/tty.h>
69 #include <linux/console.h>
70 #include <linux/string.h>
72 #include <linux/slab.h>
74 #include <linux/vt_kern.h>
75 #include <linux/selection.h>
76 #include <linux/font.h>
77 #include <linux/smp.h>
78 #include <linux/init.h>
79 #include <linux/interrupt.h>
82 #include <asm/system.h>
83 #include <asm/uaccess.h>
85 #include <asm/atariints.h>
88 #include <asm/macints.h>
90 #if defined(__mc68000__) || defined(CONFIG_APUS)
91 #include <asm/machdep.h>
92 #include <asm/setup.h>
96 #ifdef CONFIG_BOOTSPLASH
97 #include "../bootsplash/bootsplash.h"
101 # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
103 # define DPRINTK(fmt, args...)
106 struct display fb_display[MAX_NR_CONSOLES];
107 signed char con2fb_map[MAX_NR_CONSOLES];
108 static int logo_height;
109 static int logo_lines;
110 static int logo_shown = -1;
111 /* Software scrollback */
112 int fbcon_softback_size = 32768;
113 static unsigned long softback_buf, softback_curr;
114 static unsigned long softback_in;
115 static unsigned long softback_top, softback_end;
116 static int softback_lines;
117 /* console mappings */
118 static int first_fb_vc;
119 static int last_fb_vc = MAX_NR_CONSOLES - 1;
120 static int fbcon_is_default = 1;
122 static char fontname[40];
124 /* current fb_info */
125 static int info_idx = -1;
127 #define REFCOUNT(fd) (((int *)(fd))[-1])
128 #define FNTSIZE(fd) (((int *)(fd))[-2])
129 #define FNTCHARCNT(fd) (((int *)(fd))[-3])
130 #define FNTSUM(fd) (((int *)(fd))[-4])
131 #define FONT_EXTRA_WORDS 4
133 #define CM_SOFTBACK (8)
135 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
137 static void fbcon_free_font(struct display *);
138 static int fbcon_set_origin(struct vc_data *);
140 #define CURSOR_DRAW_DELAY (1)
142 /* # VBL ints between cursor state changes */
143 #define ARM_CURSOR_BLINK_RATE (10)
144 #define ATARI_CURSOR_BLINK_RATE (42)
145 #define MAC_CURSOR_BLINK_RATE (32)
146 #define DEFAULT_CURSOR_BLINK_RATE (20)
148 static int vbl_cursor_cnt;
150 #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1)
153 * Interface used by the world
156 static const char *fbcon_startup(void);
157 static void fbcon_init(struct vc_data *vc, int init);
158 static void fbcon_deinit(struct vc_data *vc);
159 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
161 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos);
162 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
163 int count, int ypos, int xpos);
164 static void fbcon_cursor(struct vc_data *vc, int mode);
165 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
167 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
168 int height, int width);
169 static int fbcon_switch(struct vc_data *vc);
170 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch);
171 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table);
172 static int fbcon_scrolldelta(struct vc_data *vc, int lines);
173 void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
180 static __inline__ int real_y(struct display *p, int ypos);
181 static __inline__ void ywrap_up(struct vc_data *vc, int count);
182 static __inline__ void ywrap_down(struct vc_data *vc, int count);
183 static __inline__ void ypan_up(struct vc_data *vc, int count);
184 static __inline__ void ypan_down(struct vc_data *vc, int count);
185 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
186 int dy, int dx, int height, int width, u_int y_break);
187 static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc);
188 static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
189 int line, int count, int dy);
193 * On the Macintoy, there may or may not be a working VBL int. We need to probe
195 static int vbl_detected;
197 static irqreturn_t fb_vbl_detect(int irq, void *dummy, struct pt_regs *fp)
204 static void fb_flashcursor(void *private)
206 struct fb_info *info = (struct fb_info *) private;
207 struct vc_data *vc = NULL;
209 if (info->currcon != -1)
210 vc = vc_cons[info->currcon].d;
212 if (info->state != FBINFO_STATE_RUNNING ||
213 info->cursor.rop == ROP_COPY || !vc || !CON_IS_VISIBLE(vc)
214 || registered_fb[(int) con2fb_map[vc->vc_num]] != info)
216 info->cursor.enable ^= 1;
217 #ifdef CONFIG_BOOTSPLASH
218 if (info->splash_data) {
219 splash_cursor(info->splash_data, info, &info->cursor);
223 acquire_console_sem();
224 info->fbops->fb_cursor(info, &info->cursor);
225 release_console_sem();
228 #if (defined(__arm__) && defined(IRQ_VSYNCPULSE)) || defined(CONFIG_ATARI) || defined(CONFIG_MAC)
229 static int cursor_blink_rate;
230 static irqreturn_t fb_vbl_handler(int irq, void *dev_id, struct pt_regs *fp)
232 struct fb_info *info = dev_id;
234 if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
235 schedule_work(&info->queue);
236 vbl_cursor_cnt = cursor_blink_rate;
242 static void cursor_timer_handler(unsigned long dev_addr)
244 struct fb_info *info = (struct fb_info *) dev_addr;
246 schedule_work(&info->queue);
247 mod_timer(&info->cursor_timer, jiffies + HZ/5);
250 int __init fb_console_setup(char *this_opt)
255 if (!this_opt || !*this_opt)
258 while ((options = strsep(&this_opt, ",")) != NULL) {
259 if (!strncmp(options, "font:", 5))
260 strcpy(fontname, options + 5);
262 if (!strncmp(options, "scrollback:", 11)) {
265 fbcon_softback_size = simple_strtoul(options, &options, 0);
266 if (*options == 'k' || *options == 'K') {
267 fbcon_softback_size *= 1024;
277 if (!strncmp(options, "map:", 4)) {
280 for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
283 con2fb_map[i] = (options[j++]-'0') % FB_MAX;
288 if (!strncmp(options, "vc:", 3)) {
291 first_fb_vc = simple_strtoul(options, &options, 10) - 1;
294 if (*options++ == '-')
295 last_fb_vc = simple_strtoul(options, &options, 10) - 1;
296 fbcon_is_default = 0;
302 __setup("fbcon=", fb_console_setup);
304 static int search_fb_in_map(int idx)
308 for (i = 0; i < MAX_NR_CONSOLES; i++) {
309 if (con2fb_map[i] == idx)
315 static int search_for_mapped_con(void)
319 for (i = 0; i < MAX_NR_CONSOLES; i++) {
320 if (con2fb_map[i] != -1)
327 * set_con2fb_map - map console to frame buffer device
328 * @unit: virtual console number to map
329 * @newidx: frame buffer index to map virtual console to
331 * Maps a virtual console @unit to a frame buffer device
334 int set_con2fb_map(int unit, int newidx)
336 struct vc_data *vc = vc_cons[unit].d;
337 int oldidx = con2fb_map[unit];
338 struct fb_info *info = registered_fb[newidx];
339 struct fb_info *oldinfo = NULL;
342 if (oldidx == newidx)
348 if (!search_for_mapped_con()) {
355 oldinfo = registered_fb[oldidx];
357 found = search_fb_in_map(newidx);
359 acquire_console_sem();
360 con2fb_map[unit] = newidx;
362 if (!try_module_get(info->fbops->owner)) {
363 con2fb_map[unit] = oldidx;
364 release_console_sem();
367 if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
368 module_put(info->fbops->owner);
369 con2fb_map[unit] = oldidx;
370 release_console_sem();
376 * If old fb is not mapped to any of the consoles,
377 * fbcon should release it.
379 if (oldinfo && !search_fb_in_map(oldidx)) {
380 if (oldinfo->fbops->fb_release &&
381 oldinfo->fbops->fb_release(oldinfo, 0)) {
382 con2fb_map[unit] = oldidx;
383 if (!found && info->fbops->fb_release)
384 info->fbops->fb_release(info, 0);
386 module_put(info->fbops->owner);
387 release_console_sem();
390 if (oldinfo->queue.func == fb_flashcursor)
391 del_timer_sync(&oldinfo->cursor_timer);
392 module_put(oldinfo->fbops->owner);
396 if (!info->queue.func || info->queue.func == fb_flashcursor) {
397 if (!info->queue.func)
398 INIT_WORK(&info->queue, fb_flashcursor, info);
400 init_timer(&info->cursor_timer);
401 info->cursor_timer.function = cursor_timer_handler;
402 info->cursor_timer.expires = jiffies + HZ / 5;
403 info->cursor_timer.data = (unsigned long ) info;
404 add_timer(&info->cursor_timer);
407 if (info->fbops->fb_set_par)
408 info->fbops->fb_set_par(info);
409 fbcon_set_disp(info, vc);
410 release_console_sem();
415 * Accelerated handlers.
417 void accel_bmove(struct vc_data *vc, struct fb_info *info, int sy,
418 int sx, int dy, int dx, int height, int width)
420 struct fb_copyarea area;
422 #ifdef CONFIG_BOOTSPLASH
423 if (info->splash_data) {
424 splash_bmove(info->splash_data, vc, info,
425 sy, sx, dy, dx, height, width);
430 area.sx = sx * vc->vc_font.width;
431 area.sy = sy * vc->vc_font.height;
432 area.dx = dx * vc->vc_font.width;
433 area.dy = dy * vc->vc_font.height;
434 area.height = height * vc->vc_font.height;
435 area.width = width * vc->vc_font.width;
437 info->fbops->fb_copyarea(info, &area);
440 void accel_clear(struct vc_data *vc, struct fb_info *info, int sy,
441 int sx, int height, int width)
443 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
444 struct fb_fillrect region;
446 #ifdef CONFIG_BOOTSPLASH
447 if (info->splash_data) {
448 splash_clear(info->splash_data, vc, info,
449 sy, sx, height, width);
453 region.color = attr_bgcol_ec(bgshift, vc);
454 region.dx = sx * vc->vc_font.width;
455 region.dy = sy * vc->vc_font.height;
456 region.width = width * vc->vc_font.width;
457 region.height = height * vc->vc_font.height;
458 region.rop = ROP_COPY;
460 info->fbops->fb_fillrect(info, ®ion);
463 void accel_putcs(struct vc_data *vc, struct fb_info *info,
464 const unsigned short *s, int count, int yy, int xx)
466 void (*move_unaligned)(struct fb_info *info, struct fb_pixmap *buf,
467 u8 *dst, u32 d_pitch, u8 *src, u32 idx,
468 u32 height, u32 shift_high, u32 shift_low,
470 void (*move_aligned)(struct fb_info *info, struct fb_pixmap *buf,
471 u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch,
473 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
474 unsigned int width = (vc->vc_font.width + 7) >> 3;
475 unsigned int cellsize = vc->vc_font.height * width;
476 unsigned int maxcnt = info->pixmap.size/cellsize;
477 unsigned int scan_align = info->pixmap.scan_align - 1;
478 unsigned int buf_align = info->pixmap.buf_align - 1;
479 unsigned int shift_low = 0, mod = vc->vc_font.width % 8;
480 unsigned int shift_high = 8, pitch, cnt, size, k;
481 unsigned int idx = vc->vc_font.width >> 3;
482 struct fb_image image;
485 #ifdef CONFIG_BOOTSPLASH
486 if (info->splash_data) {
487 splash_putcs(info->splash_data, vc, info, s, count, yy, xx);
492 image.fg_color = attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8,
494 image.bg_color = attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12,
496 image.dx = xx * vc->vc_font.width;
497 image.dy = yy * vc->vc_font.height;
498 image.height = vc->vc_font.height;
501 if (info->pixmap.outbuf && info->pixmap.inbuf) {
502 move_aligned = fb_iomove_buf_aligned;
503 move_unaligned = fb_iomove_buf_unaligned;
505 move_aligned = fb_sysmove_buf_aligned;
506 move_unaligned = fb_sysmove_buf_unaligned;
514 image.width = vc->vc_font.width * cnt;
515 pitch = ((image.width + 7) >> 3) + scan_align;
516 pitch &= ~scan_align;
517 size = pitch * image.height + buf_align;
519 dst = fb_get_buffer_offset(info, &info->pixmap, size);
523 src = vc->vc_font.data + (scr_readw(s++)&
525 move_unaligned(info, &info->pixmap, dst, pitch,
526 src, idx, image.height,
527 shift_high, shift_low, mod);
529 dst += (shift_low >= 8) ? width : width - 1;
531 shift_high = 8 - shift_low;
535 src = vc->vc_font.data + (scr_readw(s++)&
537 move_aligned(info, &info->pixmap, dst, pitch,
538 src, idx, image.height);
542 info->fbops->fb_imageblit(info, &image);
543 image.dx += cnt * vc->vc_font.width;
548 void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
551 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
552 unsigned int cw = vc->vc_font.width;
553 unsigned int ch = vc->vc_font.height;
554 unsigned int rw = info->var.xres - (vc->vc_cols*cw);
555 unsigned int bh = info->var.yres - (vc->vc_rows*ch);
556 unsigned int rs = info->var.xres - rw;
557 unsigned int bs = info->var.yres - bh;
558 struct fb_fillrect region;
560 #ifdef CONFIG_BOOTSPLASH
561 if (info->splash_data) {
562 splash_clear_margins(info->splash_data, vc, info, bottom_only);
567 region.color = attr_bgcol_ec(bgshift, vc);
568 region.rop = ROP_COPY;
570 if (rw && !bottom_only) {
571 region.dx = info->var.xoffset + rs;
574 region.height = info->var.yres_virtual;
575 info->fbops->fb_fillrect(info, ®ion);
579 region.dx = info->var.xoffset;
580 region.dy = info->var.yoffset + bs;
583 info->fbops->fb_fillrect(info, ®ion);
588 * Low Level Operations
590 /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */
591 static const char *fbcon_startup(void)
593 const char *display_desc = "frame buffer device";
594 struct display *p = &fb_display[fg_console];
595 struct vc_data *vc = vc_cons[fg_console].d;
596 struct font_desc *font = NULL;
597 struct module *owner;
598 struct fb_info *info = NULL;
604 * If num_registered_fb is zero, this is a call for the dummy part.
605 * The frame buffer devices weren't initialized yet.
607 if (!num_registered_fb || info_idx == -1)
610 * Instead of blindly using registered_fb[0], we use info_idx, set by
613 info = registered_fb[info_idx];
618 owner = info->fbops->owner;
619 if (!try_module_get(owner))
621 if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
625 if (info->fix.type != FB_TYPE_TEXT) {
626 if (fbcon_softback_size) {
630 kmalloc(fbcon_softback_size,
633 fbcon_softback_size = 0;
639 kfree((void *) softback_buf);
645 softback_in = softback_top = softback_curr =
650 /* Setup default font */
652 if (!fontname[0] || !(font = find_font(fontname)))
653 font = get_default_font(info->var.xres,
655 vc->vc_font.width = font->width;
656 vc->vc_font.height = font->height;
657 vc->vc_font.data = p->fontdata = font->data;
658 vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */
661 cols = info->var.xres / vc->vc_font.width;
662 rows = info->var.yres / vc->vc_font.height;
663 vc_resize(vc->vc_num, cols, rows);
665 DPRINTK("mode: %s\n", info->fix.id);
666 DPRINTK("visual: %d\n", info->fix.visual);
667 DPRINTK("res: %dx%d-%d\n", info->var.xres,
669 info->var.bits_per_pixel);
670 con_set_default_unimap(vc->vc_num);
674 cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
676 request_irq(IRQ_AUTO_4, fb_vbl_handler,
677 IRQ_TYPE_PRIO, "framebuffer vbl",
680 #endif /* CONFIG_ATARI */
684 * On a Macintoy, the VBL interrupt may or may not be active.
685 * As interrupt based cursor is more reliable and race free, we
686 * probe for VBL interrupts.
691 * Probe for VBL: set temp. handler ...
693 irqres = request_irq(IRQ_MAC_VBL, fb_vbl_detect, 0,
694 "framebuffer vbl", info);
698 * ... and spin for 20 ms ...
700 while (!vbl_detected && ++ct < 1000)
705 ("fbcon_startup: No VBL detected, using timer based cursor.\n");
707 free_irq(IRQ_MAC_VBL, fb_vbl_detect);
711 * interrupt based cursor ok
713 cursor_blink_rate = MAC_CURSOR_BLINK_RATE;
715 request_irq(IRQ_MAC_VBL, fb_vbl_handler, 0,
716 "framebuffer vbl", info);
719 * VBL not detected: fall through, use timer based cursor
724 #endif /* CONFIG_MAC */
726 #if defined(__arm__) && defined(IRQ_VSYNCPULSE)
727 cursor_blink_rate = ARM_CURSOR_BLINK_RATE;
728 irqres = request_irq(IRQ_VSYNCPULSE, fb_vbl_handler, SA_SHIRQ,
729 "framebuffer vbl", info);
731 /* Initialize the work queue. If the driver provides its
732 * own work queue this means it will use something besides
733 * default timer to flash the cursor. */
734 if (!info->queue.func) {
735 INIT_WORK(&info->queue, fb_flashcursor, info);
737 init_timer(&info->cursor_timer);
738 info->cursor_timer.function = cursor_timer_handler;
739 info->cursor_timer.expires = jiffies + HZ / 5;
740 info->cursor_timer.data = (unsigned long ) info;
741 add_timer(&info->cursor_timer);
746 static void fbcon_init(struct vc_data *vc, int init)
748 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
749 struct vc_data **default_mode = vc->vc_display_fg;
750 struct display *t, *p = &fb_display[vc->vc_num];
751 int display_fg = (*default_mode)->vc_num;
752 int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;
753 unsigned short *save = NULL, *r, *q;
754 int cap = info->flags;
756 if (info_idx == -1 || info == NULL)
758 if (vc->vc_num != display_fg || (info->flags & FBINFO_MODULE) ||
759 (info->fix.type == FB_TYPE_TEXT))
762 info->var.xoffset = info->var.yoffset = p->yscroll = 0; /* reset wrap/pan */
764 /* If we are not the first console on this
765 fb, copy the font from that console */
766 t = &fb_display[display_fg];
767 if (!vc->vc_font.data) {
768 vc->vc_font.data = p->fontdata = t->fontdata;
769 vc->vc_font.width = (*default_mode)->vc_font.width;
770 vc->vc_font.height = (*default_mode)->vc_font.height;
771 p->userfont = t->userfont;
773 REFCOUNT(p->fontdata)++;
774 con_copy_unimap(vc->vc_num, display_fg);
777 charcnt = FNTCHARCNT(p->fontdata);
778 vc->vc_can_do_color = info->var.bits_per_pixel != 1;
779 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
780 if (charcnt == 256) {
781 vc->vc_hi_font_mask = 0;
783 vc->vc_hi_font_mask = 0x100;
784 if (vc->vc_can_do_color)
785 vc->vc_complement_mask <<= 1;
789 new_cols = info->var.xres / vc->vc_font.width;
790 new_rows = info->var.yres / vc->vc_font.height;
792 #ifdef CONFIG_BOOTSPLASH
793 if (vc->vc_splash_data && vc->vc_splash_data->splash_state) {
794 new_cols = vc->vc_splash_data->splash_text_wi / vc->vc_font.width;
795 new_rows = vc->vc_splash_data->splash_text_he / vc->vc_font.height;
798 con_remap_def_color(vc->vc_num, vc->vc_splash_data->splash_color << 4 | vc->vc_splash_data->splash_fg_color);
802 vc_resize(vc->vc_num, new_cols, new_rows);
804 * We must always set the mode. The mode of the previous console
805 * driver could be in the same resolution but we are using different
806 * hardware so we have to initialize the hardware.
808 * We need to do it in fbcon_init() to prevent screen corruption.
810 if (CON_IS_VISIBLE(vc) && info->fbops->fb_set_par)
811 info->fbops->fb_set_par(info);
814 if ((cap & FBINFO_HWACCEL_COPYAREA) &&
815 !(cap & FBINFO_HWACCEL_DISABLED))
816 p->scrollmode = SCROLL_MOVE;
817 else /* default to something safe */
818 p->scrollmode = SCROLL_REDRAW;
821 * ++guenther: console.c:vc_allocate() relies on initializing
822 * vc_{cols,rows}, but we must not set those if we are only
823 * resizing the console.
826 vc->vc_cols = new_cols;
827 vc->vc_rows = new_rows;
831 /* Need to make room for the logo */
835 logo_height = fb_prepare_logo(info);
836 logo_lines = (logo_height + vc->vc_font.height - 1) /
838 q = (unsigned short *) (vc->vc_origin +
839 vc->vc_size_row * rows);
840 step = logo_lines * cols;
841 for (r = q - logo_lines * cols; r < q; r++)
842 if (scr_readw(r) != vc->vc_video_erase_char)
844 if (r != q && new_rows >= rows + logo_lines) {
845 save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL);
847 int i = cols < new_cols ? cols : new_cols;
848 scr_memsetw(save, vc->vc_video_erase_char,
849 logo_lines * new_cols * 2);
851 for (cnt = 0; cnt < logo_lines; cnt++, r += i)
852 scr_memcpyw(save + cnt * new_cols, r, 2 * i);
857 /* We can scroll screen down */
859 for (cnt = rows - logo_lines; cnt > 0; cnt--) {
860 scr_memcpyw(r + step, r, vc->vc_size_row);
864 vc->vc_y += logo_lines;
865 vc->vc_pos += logo_lines * vc->vc_size_row;
868 scr_memsetw((unsigned short *) vc->vc_origin,
869 vc->vc_video_erase_char,
870 vc->vc_size_row * logo_lines);
872 if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
873 accel_clear_margins(vc, info, 0);
874 update_screen(vc->vc_num);
877 q = (unsigned short *) (vc->vc_origin +
880 scr_memcpyw(q, save, logo_lines * new_cols * 2);
881 vc->vc_y += logo_lines;
882 vc->vc_pos += logo_lines * vc->vc_size_row;
885 if (logo_lines > vc->vc_bottom) {
888 "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
891 vc->vc_top = logo_lines;
895 if (vc->vc_num == display_fg && softback_buf) {
896 int l = fbcon_softback_size / vc->vc_size_row;
898 softback_end = softback_buf + l * vc->vc_size_row;
900 /* Smaller scrollback makes no sense, and 0 would screw
901 the operation totally */
907 static void fbcon_deinit(struct vc_data *vc)
909 struct display *p = &fb_display[vc->vc_num];
916 /* ====================================================================== */
918 /* fbcon_XXX routines - interface used by the world
920 * This system is now divided into two levels because of complications
921 * caused by hardware scrolling. Top level functions:
923 * fbcon_bmove(), fbcon_clear(), fbcon_putc()
925 * handles y values in range [0, scr_height-1] that correspond to real
926 * screen positions. y_wrap shift means that first line of bitmap may be
927 * anywhere on this display. These functions convert lineoffsets to
928 * bitmap offsets and deal with the wrap-around case by splitting blits.
930 * fbcon_bmove_physical_8() -- These functions fast implementations
931 * fbcon_clear_physical_8() -- of original fbcon_XXX fns.
932 * fbcon_putc_physical_8() -- (font width != 8) may be added later
936 * At the moment fbcon_putc() cannot blit across vertical wrap boundary
937 * Implies should only really hardware scroll in rows. Only reason for
938 * restriction is simplicity & efficiency at the moment.
941 static __inline__ int real_y(struct display *p, int ypos)
946 return ypos < rows ? ypos : ypos - rows;
950 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
953 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
955 struct display *p = &fb_display[vc->vc_num];
958 if (!info->fbops->fb_blank && console_blanked)
960 if (info->state != FBINFO_STATE_RUNNING)
963 if (!height || !width)
966 /* Split blits that cross physical y_wrap boundary */
968 y_break = p->vrows - p->yscroll;
969 if (sy < y_break && sy + height - 1 >= y_break) {
970 u_int b = y_break - sy;
971 accel_clear(vc, info, real_y(p, sy), sx, b, width);
972 accel_clear(vc, info, real_y(p, sy + b), sx, height - b,
975 accel_clear(vc, info, real_y(p, sy), sx, height, width);
978 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
980 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
981 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
982 unsigned int scan_align = info->pixmap.scan_align - 1;
983 unsigned int buf_align = info->pixmap.buf_align - 1;
984 unsigned int width = (vc->vc_font.width + 7) >> 3;
985 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
986 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
987 struct display *p = &fb_display[vc->vc_num];
988 unsigned int size, pitch;
989 struct fb_image image;
992 if (!info->fbops->fb_blank && console_blanked)
994 if (info->state != FBINFO_STATE_RUNNING)
997 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
1000 #ifdef CONFIG_BOOTSPLASH
1001 if (info->splash_data) {
1002 splash_putc(info->splash_data, vc, info, c, ypos, xpos);
1007 image.dx = xpos * vc->vc_font.width;
1008 image.dy = real_y(p, ypos) * vc->vc_font.height;
1009 image.width = vc->vc_font.width;
1010 image.height = vc->vc_font.height;
1011 image.fg_color = attr_fgcol(fgshift, c);
1012 image.bg_color = attr_bgcol(bgshift, c);
1015 src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * width;
1017 pitch = width + scan_align;
1018 pitch &= ~scan_align;
1019 size = pitch * vc->vc_font.height;
1023 dst = fb_get_buffer_offset(info, &info->pixmap, size);
1026 if (info->pixmap.outbuf)
1027 fb_iomove_buf_aligned(info, &info->pixmap, dst, pitch, src, width, image.height);
1029 fb_sysmove_buf_aligned(info, &info->pixmap, dst, pitch, src, width, image.height);
1031 info->fbops->fb_imageblit(info, &image);
1034 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
1035 int count, int ypos, int xpos)
1037 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1038 struct display *p = &fb_display[vc->vc_num];
1040 if (!info->fbops->fb_blank && console_blanked)
1042 if (info->state != FBINFO_STATE_RUNNING)
1045 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
1048 accel_putcs(vc, info, s, count, real_y(p, ypos), xpos);
1051 static void fbcon_cursor(struct vc_data *vc, int mode)
1053 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1054 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
1055 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
1056 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
1057 struct display *p = &fb_display[vc->vc_num];
1058 int w = (vc->vc_font.width + 7) >> 3, c;
1059 int y = real_y(p, vc->vc_y);
1060 struct fb_cursor cursor;
1062 if (mode & CM_SOFTBACK) {
1063 mode &= ~CM_SOFTBACK;
1064 if (softback_lines) {
1065 if (y + softback_lines >= vc->vc_rows)
1068 y += softback_lines;
1070 } else if (softback_lines)
1071 fbcon_set_origin(vc);
1073 c = scr_readw((u16 *) vc->vc_pos);
1075 cursor.image.data = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
1076 cursor.set = FB_CUR_SETCUR;
1077 cursor.image.depth = 1;
1081 if (info->cursor.rop == ROP_XOR) {
1082 info->cursor.enable = 0;
1083 info->cursor.rop = ROP_COPY;
1084 #ifdef CONFIG_BOOTSPLASH
1085 if (info->splash_data) {
1086 splash_cursor(info->splash_data, info, &cursor);
1090 info->fbops->fb_cursor(info, &cursor);
1095 info->cursor.enable = 1;
1097 if (info->cursor.image.fg_color != attr_fgcol(fgshift, c) ||
1098 info->cursor.image.bg_color != attr_bgcol(bgshift, c)) {
1099 cursor.image.fg_color = attr_fgcol(fgshift, c);
1100 cursor.image.bg_color = attr_bgcol(bgshift, c);
1101 cursor.set |= FB_CUR_SETCMAP;
1104 if ((info->cursor.image.dx != (vc->vc_font.width * vc->vc_x)) ||
1105 (info->cursor.image.dy != (vc->vc_font.height * y))) {
1106 cursor.image.dx = vc->vc_font.width * vc->vc_x;
1107 cursor.image.dy = vc->vc_font.height * y;
1108 cursor.set |= FB_CUR_SETPOS;
1111 if (info->cursor.image.height != vc->vc_font.height ||
1112 info->cursor.image.width != vc->vc_font.width) {
1113 cursor.image.height = vc->vc_font.height;
1114 cursor.image.width = vc->vc_font.width;
1115 cursor.set |= FB_CUR_SETSIZE;
1118 if (info->cursor.hot.x || info->cursor.hot.y) {
1119 cursor.hot.x = cursor.hot.y = 0;
1120 cursor.set |= FB_CUR_SETHOT;
1123 if ((cursor.set & FB_CUR_SETSIZE) || ((vc->vc_cursor_type & 0x0f) != p->cursor_shape)
1124 || info->cursor.mask == NULL) {
1125 char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
1126 int cur_height, size, i = 0;
1130 if (info->cursor.mask)
1131 kfree(info->cursor.mask);
1132 info->cursor.mask = mask;
1134 p->cursor_shape = vc->vc_cursor_type & 0x0f;
1135 cursor.set |= FB_CUR_SETSHAPE;
1137 switch (vc->vc_cursor_type & 0x0f) {
1142 cur_height = (vc->vc_font.height < 10) ? 1 : 2;
1144 case CUR_LOWER_THIRD:
1145 cur_height = vc->vc_font.height/3;
1147 case CUR_LOWER_HALF:
1148 cur_height = vc->vc_font.height >> 1;
1150 case CUR_TWO_THIRDS:
1151 cur_height = (vc->vc_font.height << 1)/3;
1155 cur_height = vc->vc_font.height;
1158 size = (vc->vc_font.height - cur_height) * w;
1161 size = cur_height * w;
1165 info->cursor.rop = ROP_XOR;
1166 #ifdef CONFIG_BOOTSPLASH
1167 if (info->splash_data) {
1168 splash_cursor(info->splash_data, info, &cursor);
1169 vbl_cursor_cnt = CURSOR_DRAW_DELAY;
1173 info->fbops->fb_cursor(info, &cursor);
1174 vbl_cursor_cnt = CURSOR_DRAW_DELAY;
1179 static int scrollback_phys_max = 0;
1180 static int scrollback_max = 0;
1181 static int scrollback_current = 0;
1183 int update_var(int con, struct fb_info *info)
1185 if (con == info->currcon)
1186 return fb_pan_display(info, &info->var);
1190 static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc)
1192 struct display *p = &fb_display[vc->vc_num], *t;
1193 struct vc_data **default_mode = vc->vc_display_fg;
1194 int display_fg = (*default_mode)->vc_num;
1195 int rows, cols, charcnt = 256;
1197 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
1198 t = &fb_display[display_fg];
1199 if (!vc->vc_font.data) {
1200 vc->vc_font.data = p->fontdata = t->fontdata;
1201 vc->vc_font.width = (*default_mode)->vc_font.width;
1202 vc->vc_font.height = (*default_mode)->vc_font.height;
1203 p->userfont = t->userfont;
1205 REFCOUNT(p->fontdata)++;
1206 con_copy_unimap(vc->vc_num, display_fg);
1209 charcnt = FNTCHARCNT(p->fontdata);
1211 vc->vc_can_do_color = info->var.bits_per_pixel != 1;
1212 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1213 if (charcnt == 256) {
1214 vc->vc_hi_font_mask = 0;
1216 vc->vc_hi_font_mask = 0x100;
1217 if (vc->vc_can_do_color)
1218 vc->vc_complement_mask <<= 1;
1220 cols = info->var.xres / vc->vc_font.width;
1221 rows = info->var.yres / vc->vc_font.height;
1222 vc_resize(vc->vc_num, cols, rows);
1223 if (CON_IS_VISIBLE(vc)) {
1224 update_screen(vc->vc_num);
1226 int l = fbcon_softback_size / vc->vc_size_row;
1229 softback_end = softback_buf + l *
1232 /* Smaller scrollback makes no sense, and 0
1233 would screw the operation totally */
1238 switch_screen(fg_console);
1241 static __inline__ void ywrap_up(struct vc_data *vc, int count)
1243 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1244 struct display *p = &fb_display[vc->vc_num];
1246 p->yscroll += count;
1247 if (p->yscroll >= p->vrows) /* Deal with wrap */
1248 p->yscroll -= p->vrows;
1249 info->var.xoffset = 0;
1250 info->var.yoffset = p->yscroll * vc->vc_font.height;
1251 info->var.vmode |= FB_VMODE_YWRAP;
1252 update_var(vc->vc_num, info);
1253 scrollback_max += count;
1254 if (scrollback_max > scrollback_phys_max)
1255 scrollback_max = scrollback_phys_max;
1256 scrollback_current = 0;
1259 static __inline__ void ywrap_down(struct vc_data *vc, int count)
1261 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1262 struct display *p = &fb_display[vc->vc_num];
1264 p->yscroll -= count;
1265 if (p->yscroll < 0) /* Deal with wrap */
1266 p->yscroll += p->vrows;
1267 info->var.xoffset = 0;
1268 info->var.yoffset = p->yscroll * vc->vc_font.height;
1269 info->var.vmode |= FB_VMODE_YWRAP;
1270 update_var(vc->vc_num, info);
1271 scrollback_max -= count;
1272 if (scrollback_max < 0)
1274 scrollback_current = 0;
1277 static __inline__ void ypan_up(struct vc_data *vc, int count)
1279 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1280 struct display *p = &fb_display[vc->vc_num];
1282 p->yscroll += count;
1283 if (p->yscroll > p->vrows - vc->vc_rows) {
1284 accel_bmove(vc, info, p->vrows - vc->vc_rows,
1285 0, 0, 0, vc->vc_rows, vc->vc_cols);
1286 p->yscroll -= p->vrows - vc->vc_rows;
1288 info->var.xoffset = 0;
1289 info->var.yoffset = p->yscroll * vc->vc_font.height;
1290 info->var.vmode &= ~FB_VMODE_YWRAP;
1291 update_var(vc->vc_num, info);
1292 accel_clear_margins(vc, info, 1);
1293 scrollback_max += count;
1294 if (scrollback_max > scrollback_phys_max)
1295 scrollback_max = scrollback_phys_max;
1296 scrollback_current = 0;
1299 static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
1301 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1302 struct display *p = &fb_display[vc->vc_num];
1305 p->yscroll += count;
1306 if (p->yscroll > p->vrows - vc->vc_rows) {
1307 p->yscroll -= p->vrows - vc->vc_rows;
1311 info->var.xoffset = 0;
1312 info->var.yoffset = p->yscroll * vc->vc_font.height;
1313 info->var.vmode &= ~FB_VMODE_YWRAP;
1315 fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
1316 update_var(vc->vc_num, info);
1317 accel_clear_margins(vc, info, 1);
1318 scrollback_max += count;
1319 if (scrollback_max > scrollback_phys_max)
1320 scrollback_max = scrollback_phys_max;
1321 scrollback_current = 0;
1324 static __inline__ void ypan_down(struct vc_data *vc, int count)
1326 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1327 struct display *p = &fb_display[vc->vc_num];
1329 p->yscroll -= count;
1330 if (p->yscroll < 0) {
1331 accel_bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
1332 0, vc->vc_rows, vc->vc_cols);
1333 p->yscroll += p->vrows - vc->vc_rows;
1335 info->var.xoffset = 0;
1336 info->var.yoffset = p->yscroll * vc->vc_font.height;
1337 info->var.vmode &= ~FB_VMODE_YWRAP;
1338 update_var(vc->vc_num, info);
1339 accel_clear_margins(vc, info, 1);
1340 scrollback_max -= count;
1341 if (scrollback_max < 0)
1343 scrollback_current = 0;
1346 static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
1348 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1349 struct display *p = &fb_display[vc->vc_num];
1352 p->yscroll -= count;
1353 if (p->yscroll < 0) {
1354 p->yscroll += p->vrows - vc->vc_rows;
1357 info->var.xoffset = 0;
1358 info->var.yoffset = p->yscroll * vc->vc_font.height;
1359 info->var.vmode &= ~FB_VMODE_YWRAP;
1361 fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
1362 update_var(vc->vc_num, info);
1363 accel_clear_margins(vc, info, 1);
1364 scrollback_max -= count;
1365 if (scrollback_max < 0)
1367 scrollback_current = 0;
1370 static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
1373 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1374 int count = vc->vc_rows;
1375 unsigned short *d, *s;
1379 d = (u16 *) softback_curr;
1380 if (d == (u16 *) softback_in)
1381 d = (u16 *) vc->vc_origin;
1382 n = softback_curr + delta * vc->vc_size_row;
1383 softback_lines -= delta;
1385 if (softback_curr < softback_top && n < softback_buf) {
1386 n += softback_end - softback_buf;
1387 if (n < softback_top) {
1389 (softback_top - n) / vc->vc_size_row;
1392 } else if (softback_curr >= softback_top
1393 && n < softback_top) {
1395 (softback_top - n) / vc->vc_size_row;
1399 if (softback_curr > softback_in && n >= softback_end) {
1400 n += softback_buf - softback_end;
1401 if (n > softback_in) {
1405 } else if (softback_curr <= softback_in && n > softback_in) {
1410 if (n == softback_curr)
1413 s = (u16 *) softback_curr;
1414 if (s == (u16 *) softback_in)
1415 s = (u16 *) vc->vc_origin;
1417 unsigned short *start;
1421 unsigned short attr = 1;
1424 le = advance_row(s, 1);
1427 if (attr != (c & 0xff00)) {
1430 accel_putcs(vc, info, start, s - start,
1431 real_y(p, line), x);
1436 if (c == scr_readw(d)) {
1438 accel_putcs(vc, info, start, s - start,
1439 real_y(p, line), x);
1451 accel_putcs(vc, info, start, s - start,
1452 real_y(p, line), x);
1454 if (d == (u16 *) softback_end)
1455 d = (u16 *) softback_buf;
1456 if (d == (u16 *) softback_in)
1457 d = (u16 *) vc->vc_origin;
1458 if (s == (u16 *) softback_end)
1459 s = (u16 *) softback_buf;
1460 if (s == (u16 *) softback_in)
1461 s = (u16 *) vc->vc_origin;
1465 static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
1466 int line, int count, int dy)
1468 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1469 unsigned short *s = (unsigned short *)
1470 (vc->vc_origin + vc->vc_size_row * line);
1473 unsigned short *start = s;
1474 unsigned short *le = advance_row(s, 1);
1477 unsigned short attr = 1;
1481 if (attr != (c & 0xff00)) {
1484 accel_putcs(vc, info, start, s - start,
1490 console_conditional_schedule();
1494 accel_putcs(vc, info, start, s - start,
1496 console_conditional_schedule();
1501 static void fbcon_redraw(struct vc_data *vc, struct display *p,
1502 int line, int count, int offset)
1504 unsigned short *d = (unsigned short *)
1505 (vc->vc_origin + vc->vc_size_row * line);
1506 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1507 unsigned short *s = d + offset;
1510 unsigned short *start = s;
1511 unsigned short *le = advance_row(s, 1);
1514 unsigned short attr = 1;
1518 if (attr != (c & 0xff00)) {
1521 accel_putcs(vc, info, start, s - start,
1522 real_y(p, line), x);
1527 if (c == scr_readw(d)) {
1529 accel_putcs(vc, info, start, s - start,
1530 real_y(p, line), x);
1539 console_conditional_schedule();
1544 accel_putcs(vc, info, start, s - start,
1545 real_y(p, line), x);
1546 console_conditional_schedule();
1551 /* NOTE: We subtract two lines from these pointers */
1552 s -= vc->vc_size_row;
1553 d -= vc->vc_size_row;
1558 static inline void fbcon_softback_note(struct vc_data *vc, int t,
1563 if (vc->vc_num != fg_console)
1565 p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
1568 scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row);
1570 p = advance_row(p, 1);
1571 softback_in += vc->vc_size_row;
1572 if (softback_in == softback_end)
1573 softback_in = softback_buf;
1574 if (softback_in == softback_top) {
1575 softback_top += vc->vc_size_row;
1576 if (softback_top == softback_end)
1577 softback_top = softback_buf;
1580 softback_curr = softback_in;
1583 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
1586 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1587 struct display *p = &fb_display[vc->vc_num];
1588 int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
1590 if (!info->fbops->fb_blank && console_blanked)
1593 if (!count || vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
1596 fbcon_cursor(vc, CM_ERASE);
1599 * ++Geert: Only use ywrap/ypan if the console is in text mode
1600 * ++Andrew: Only use ypan on hardware text mode when scrolling the
1601 * whole screen (prevents flicker).
1606 if (count > vc->vc_rows) /* Maximum realistic size */
1607 count = vc->vc_rows;
1609 fbcon_softback_note(vc, t, count);
1610 if (logo_shown >= 0)
1612 #ifdef CONFIG_BOOTSPLASH
1613 if (info->splash_data)
1616 switch (p->scrollmode) {
1618 accel_bmove(vc, info, t + count, 0, t, 0,
1619 b - t - count, vc->vc_cols);
1620 accel_clear(vc, info, b - count, 0, count,
1624 case SCROLL_WRAP_MOVE:
1625 if (b - t - count > 3 * vc->vc_rows >> 2) {
1627 fbcon_bmove(vc, 0, 0, count, 0, t,
1629 ywrap_up(vc, count);
1630 if (vc->vc_rows - b > 0)
1631 fbcon_bmove(vc, b - count, 0, b, 0,
1634 } else if (info->flags & FBINFO_READS_FAST)
1635 fbcon_bmove(vc, t + count, 0, t, 0,
1636 b - t - count, vc->vc_cols);
1639 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1642 case SCROLL_PAN_REDRAW:
1643 if ((p->yscroll + count <=
1644 2 * (p->vrows - vc->vc_rows))
1645 && ((!scroll_partial && (b - t == vc->vc_rows))
1648 3 * vc->vc_rows >> 2)))) {
1650 fbcon_redraw_move(vc, p, 0, t, count);
1651 ypan_up_redraw(vc, t, count);
1652 if (vc->vc_rows - b > 0)
1653 fbcon_redraw_move(vc, p, b - count,
1654 vc->vc_rows - b, b);
1656 fbcon_redraw_move(vc, p, t + count, b - t - count, t);
1657 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1660 case SCROLL_PAN_MOVE:
1661 if ((p->yscroll + count <=
1662 2 * (p->vrows - vc->vc_rows))
1663 && ((!scroll_partial && (b - t == vc->vc_rows))
1666 3 * vc->vc_rows >> 2)))) {
1668 fbcon_bmove(vc, 0, 0, count, 0, t,
1671 if (vc->vc_rows - b > 0)
1672 fbcon_bmove(vc, b - count, 0, b, 0,
1675 } else if (info->flags & FBINFO_READS_FAST)
1676 fbcon_bmove(vc, t + count, 0, t, 0,
1677 b - t - count, vc->vc_cols);
1680 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1685 fbcon_redraw(vc, p, t, b - t - count,
1686 count * vc->vc_cols);
1687 accel_clear(vc, info, real_y(p, b - count), 0,
1688 count, vc->vc_cols);
1689 scr_memsetw((unsigned short *) (vc->vc_origin +
1692 vc->vc_video_erase_char,
1693 vc->vc_size_row * count);
1699 if (count > vc->vc_rows) /* Maximum realistic size */
1700 count = vc->vc_rows;
1701 #ifdef CONFIG_BOOTSPLASH
1702 if (info->splash_data)
1705 switch (p->scrollmode) {
1707 accel_bmove(vc, info, t, 0, t + count, 0,
1708 b - t - count, vc->vc_cols);
1709 accel_clear(vc, info, t, 0, count, vc->vc_cols);
1712 case SCROLL_WRAP_MOVE:
1713 if (b - t - count > 3 * vc->vc_rows >> 2) {
1714 if (vc->vc_rows - b > 0)
1715 fbcon_bmove(vc, b, 0, b - count, 0,
1718 ywrap_down(vc, count);
1720 fbcon_bmove(vc, count, 0, 0, 0, t,
1722 } else if (info->flags & FBINFO_READS_FAST)
1723 fbcon_bmove(vc, t, 0, t + count, 0,
1724 b - t - count, vc->vc_cols);
1727 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1730 case SCROLL_PAN_MOVE:
1731 if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1732 && ((!scroll_partial && (b - t == vc->vc_rows))
1735 3 * vc->vc_rows >> 2)))) {
1736 if (vc->vc_rows - b > 0)
1737 fbcon_bmove(vc, b, 0, b - count, 0,
1740 ypan_down(vc, count);
1742 fbcon_bmove(vc, count, 0, 0, 0, t,
1744 } else if (info->flags & FBINFO_READS_FAST)
1745 fbcon_bmove(vc, t, 0, t + count, 0,
1746 b - t - count, vc->vc_cols);
1749 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1752 case SCROLL_PAN_REDRAW:
1753 if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1754 && ((!scroll_partial && (b - t == vc->vc_rows))
1757 3 * vc->vc_rows >> 2)))) {
1758 if (vc->vc_rows - b > 0)
1759 fbcon_redraw_move(vc, p, b, vc->vc_rows - b,
1761 ypan_down_redraw(vc, t, count);
1763 fbcon_redraw_move(vc, p, count, t, 0);
1765 fbcon_redraw_move(vc, p, t, b - t - count, t + count);
1766 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1771 fbcon_redraw(vc, p, b - 1, b - t - count,
1772 -count * vc->vc_cols);
1773 accel_clear(vc, info, real_y(p, t), 0, count,
1775 scr_memsetw((unsigned short *) (vc->vc_origin +
1778 vc->vc_video_erase_char,
1779 vc->vc_size_row * count);
1787 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
1788 int height, int width)
1790 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1791 struct display *p = &fb_display[vc->vc_num];
1793 if (!info->fbops->fb_blank && console_blanked)
1796 if (!width || !height)
1799 /* Split blits that cross physical y_wrap case.
1800 * Pathological case involves 4 blits, better to use recursive
1801 * code rather than unrolled case
1803 * Recursive invocations don't need to erase the cursor over and
1804 * over again, so we use fbcon_bmove_rec()
1806 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
1807 p->vrows - p->yscroll);
1810 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
1811 int dy, int dx, int height, int width, u_int y_break)
1813 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1816 if (sy < y_break && sy + height > y_break) {
1818 if (dy < sy) { /* Avoid trashing self */
1819 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1821 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1822 height - b, width, y_break);
1824 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1825 height - b, width, y_break);
1826 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1832 if (dy < y_break && dy + height > y_break) {
1834 if (dy < sy) { /* Avoid trashing self */
1835 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1837 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1838 height - b, width, y_break);
1840 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1841 height - b, width, y_break);
1842 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1847 #ifdef CONFIG_BOOTSPLASH
1848 if (info->splash_data && sy == dy && height == 1) {
1849 /* must use slower redraw bmove to keep background pic intact */
1850 splash_bmove_redraw(info->splash_data, vc, info, sy, sx, dx, width);
1854 accel_bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
1858 static __inline__ void updatescrollmode(struct display *p, struct fb_info *info,
1861 int fh = vc->vc_font.height;
1862 int cap = info->flags;
1863 int good_pan = (cap & FBINFO_HWACCEL_YPAN)
1864 && divides(info->fix.ypanstep, vc->vc_font.height)
1865 && info->var.yres_virtual > info->var.yres;
1866 int good_wrap = (cap & FBINFO_HWACCEL_YWRAP)
1867 && divides(info->fix.ywrapstep, vc->vc_font.height)
1868 && divides(vc->vc_font.height, info->var.yres_virtual);
1869 int reading_fast = cap & FBINFO_READS_FAST;
1870 int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) && !(cap & FBINFO_HWACCEL_DISABLED);
1871 int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) && !(cap & FBINFO_HWACCEL_DISABLED);
1873 p->vrows = info->var.yres_virtual/fh;
1874 if (info->var.yres > (fh * (vc->vc_rows + 1)))
1875 p->vrows -= (info->var.yres - (fh * vc->vc_rows)) / fh;
1876 if ((info->var.yres % fh) && (info->var.yres_virtual % fh <
1877 info->var.yres % fh))
1880 if (good_wrap || good_pan) {
1881 if (reading_fast || fast_copyarea)
1882 p->scrollmode = good_wrap ? SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
1884 p->scrollmode = good_wrap ? SCROLL_REDRAW :
1887 if (reading_fast || (fast_copyarea && !fast_imageblit))
1888 p->scrollmode = SCROLL_MOVE;
1890 p->scrollmode = SCROLL_REDRAW;
1894 static int fbcon_resize(struct vc_data *vc, unsigned int width,
1895 unsigned int height)
1897 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1898 struct display *p = &fb_display[vc->vc_num];
1899 struct fb_var_screeninfo var = info->var;
1900 int err; int x_diff, y_diff;
1901 int fw = vc->vc_font.width;
1902 int fh = vc->vc_font.height;
1904 var.xres = width * fw;
1905 var.yres = height * fh;
1906 x_diff = info->var.xres - var.xres;
1907 y_diff = info->var.yres - var.yres;
1908 if (x_diff < 0 || x_diff > fw || (y_diff < 0 || y_diff > fh)) {
1911 DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
1912 snprintf(mode, 40, "%ix%i", var.xres, var.yres);
1913 err = fb_find_mode(&var, info, mode, info->monspecs.modedb,
1914 info->monspecs.modedb_len, NULL,
1915 info->var.bits_per_pixel);
1916 if (!err || width > var.xres/fw || height > var.yres/fh)
1918 DPRINTK("resize now %ix%i\n", var.xres, var.yres);
1919 if (CON_IS_VISIBLE(vc)) {
1920 var.activate = FB_ACTIVATE_NOW |
1922 fb_set_var(info, &var);
1923 info->flags &= ~FBINFO_MISC_MODESWITCH;
1926 updatescrollmode(p, info, vc);
1930 static int fbcon_switch(struct vc_data *vc)
1932 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1933 struct display *p = &fb_display[vc->vc_num];
1936 #ifdef CONFIG_BOOTSPLASH
1937 splash_prepare(vc, info);
1941 int l = fbcon_softback_size / vc->vc_size_row;
1943 fbcon_set_origin(vc);
1944 softback_top = softback_curr = softback_in = softback_buf;
1948 softback_end = softback_buf + l * vc->vc_size_row;
1950 /* Smaller scrollback makes no sense, and 0 would screw
1951 the operation totally */
1955 if (logo_shown >= 0) {
1956 struct vc_data *conp2 = vc_cons[logo_shown].d;
1958 if (conp2->vc_top == logo_lines
1959 && conp2->vc_bottom == conp2->vc_rows)
1964 info->var.yoffset = p->yscroll = 0;
1967 * FIXME: If we have multiple fbdev's loaded, we need to
1968 * update all info->currcon. Perhaps, we can place this
1969 * in a centralized structure, but this might break some
1972 * info->currcon = vc->vc_num;
1974 for (i = 0; i < FB_MAX; i++) {
1975 if (registered_fb[i] != NULL)
1976 registered_fb[i]->currcon = vc->vc_num;
1979 fbcon_resize(vc, vc->vc_cols, vc->vc_rows);
1981 if (info->flags & FBINFO_MISC_MODESWITCH) {
1982 if (info->fbops->fb_set_par)
1983 info->fbops->fb_set_par(info);
1984 info->flags &= ~FBINFO_MISC_MODESWITCH;
1987 switch (p->scrollmode) {
1988 case SCROLL_WRAP_MOVE:
1989 scrollback_phys_max = p->vrows - vc->vc_rows;
1991 case SCROLL_PAN_MOVE:
1992 case SCROLL_PAN_REDRAW:
1993 scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
1994 if (scrollback_phys_max < 0)
1995 scrollback_phys_max = 0;
1998 scrollback_phys_max = 0;
2002 scrollback_current = 0;
2004 update_var(vc->vc_num, info);
2005 fbcon_set_palette(vc, color_table);
2007 if (vt_cons[vc->vc_num]->vc_mode == KD_TEXT)
2008 accel_clear_margins(vc, info, 0);
2009 if (logo_shown == -2) {
2010 logo_shown = fg_console;
2011 /* This is protected above by initmem_freed */
2013 update_region(fg_console,
2014 vc->vc_origin + vc->vc_size_row * vc->vc_top,
2015 vc->vc_size_row * (vc->vc_bottom -
2022 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
2024 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
2025 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2026 struct display *p = &fb_display[vc->vc_num];
2029 struct fb_var_screeninfo var = info->var;
2032 * HACK ALERT: Some hardware will require reinitializion at this stage,
2033 * others will require it to be done as late as possible.
2034 * For now, we differentiate this with the
2035 * FBINFO_MISC_MODESWITCHLATE bitflag. Worst case will be
2036 * hardware that requires it here and another one later.
2037 * A definitive solution may require fixing X or the VT
2040 if (info->flags & FBINFO_MISC_MODESWITCHLATE)
2041 info->flags |= FBINFO_MISC_MODESWITCH;
2044 fbcon_cursor(vc, CM_ERASE);
2048 if (!(info->flags & FBINFO_MISC_MODESWITCHLATE)) {
2049 var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
2050 fb_set_var(info, &var);
2054 fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
2056 if (!info->fbops->fb_blank) {
2057 #ifdef CONFIG_BOOTSPLASH
2058 if (info->splash_data) {
2059 splash_blank(info->splash_data, vc, info, blank);
2064 unsigned short oldc;
2068 oldc = vc->vc_video_erase_char;
2069 vc->vc_video_erase_char &= charmask;
2070 height = vc->vc_rows;
2071 y_break = p->vrows - p->yscroll;
2072 if (height > y_break) {
2073 accel_clear(vc, info, real_y(p, 0),
2074 0, y_break, vc->vc_cols);
2075 accel_clear(vc, info, real_y(p, y_break),
2076 0, height - y_break,
2079 accel_clear(vc, info, real_y(p, 0),
2080 0, height, vc->vc_cols);
2081 vc->vc_video_erase_char = oldc;
2083 update_screen(vc->vc_num);
2086 return fb_blank(info, blank);
2089 static void fbcon_free_font(struct display *p)
2091 if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
2092 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
2097 static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
2099 u8 *fontdata = vc->vc_font.data;
2100 u8 *data = font->data;
2103 font->width = vc->vc_font.width;
2104 font->height = vc->vc_font.height;
2105 font->charcount = vc->vc_hi_font_mask ? 512 : 256;
2109 if (font->width <= 8) {
2110 j = vc->vc_font.height;
2111 for (i = 0; i < font->charcount; i++) {
2112 memcpy(data, fontdata, j);
2113 memset(data + j, 0, 32 - j);
2117 } else if (font->width <= 16) {
2118 j = vc->vc_font.height * 2;
2119 for (i = 0; i < font->charcount; i++) {
2120 memcpy(data, fontdata, j);
2121 memset(data + j, 0, 64 - j);
2125 } else if (font->width <= 24) {
2126 for (i = 0; i < font->charcount; i++) {
2127 for (j = 0; j < vc->vc_font.height; j++) {
2128 *data++ = fontdata[0];
2129 *data++ = fontdata[1];
2130 *data++ = fontdata[2];
2131 fontdata += sizeof(u32);
2133 memset(data, 0, 3 * (32 - j));
2134 data += 3 * (32 - j);
2137 j = vc->vc_font.height * 4;
2138 for (i = 0; i < font->charcount; i++) {
2139 memcpy(data, fontdata, j);
2140 memset(data + j, 0, 128 - j);
2148 static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
2149 u8 * data, int userfont)
2151 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2152 struct display *p = &fb_display[vc->vc_num];
2155 char *old_data = NULL;
2157 if (CON_IS_VISIBLE(vc) && softback_lines)
2158 fbcon_set_origin(vc);
2160 resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
2162 old_data = vc->vc_font.data;
2164 cnt = FNTCHARCNT(data);
2167 vc->vc_font.data = p->fontdata = data;
2168 if ((p->userfont = userfont))
2170 vc->vc_font.width = w;
2171 vc->vc_font.height = h;
2172 if (vc->vc_hi_font_mask && cnt == 256) {
2173 vc->vc_hi_font_mask = 0;
2174 if (vc->vc_can_do_color) {
2175 vc->vc_complement_mask >>= 1;
2176 vc->vc_s_complement_mask >>= 1;
2179 /* ++Edmund: reorder the attribute bits */
2180 if (vc->vc_can_do_color) {
2181 unsigned short *cp =
2182 (unsigned short *) vc->vc_origin;
2183 int count = vc->vc_screenbuf_size / 2;
2185 for (; count > 0; count--, cp++) {
2187 scr_writew(((c & 0xfe00) >> 1) |
2190 c = vc->vc_video_erase_char;
2191 vc->vc_video_erase_char =
2192 ((c & 0xfe00) >> 1) | (c & 0xff);
2195 } else if (!vc->vc_hi_font_mask && cnt == 512) {
2196 vc->vc_hi_font_mask = 0x100;
2197 if (vc->vc_can_do_color) {
2198 vc->vc_complement_mask <<= 1;
2199 vc->vc_s_complement_mask <<= 1;
2202 /* ++Edmund: reorder the attribute bits */
2204 unsigned short *cp =
2205 (unsigned short *) vc->vc_origin;
2206 int count = vc->vc_screenbuf_size / 2;
2208 for (; count > 0; count--, cp++) {
2209 unsigned short newc;
2211 if (vc->vc_can_do_color)
2213 ((c & 0xff00) << 1) | (c &
2217 scr_writew(newc, cp);
2219 c = vc->vc_video_erase_char;
2220 if (vc->vc_can_do_color) {
2221 vc->vc_video_erase_char =
2222 ((c & 0xff00) << 1) | (c & 0xff);
2225 vc->vc_video_erase_char = c & ~0x100;
2231 u32 xres = info->var.xres, yres = info->var.yres;
2232 /* reset wrap/pan */
2233 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
2234 #ifdef CONFIG_BOOTSPLASH
2235 if (info->splash_data) {
2236 xres = info->splash_data->splash_text_wi;
2237 yres = info->splash_data->splash_text_he;
2240 vc_resize(vc->vc_num, xres / w, yres / h);
2241 if (CON_IS_VISIBLE(vc) && softback_buf) {
2242 int l = fbcon_softback_size / vc->vc_size_row;
2245 softback_buf + l * vc->vc_size_row;
2247 /* Smaller scrollback makes no sense, and 0 would screw
2248 the operation totally */
2252 } else if (CON_IS_VISIBLE(vc)
2253 && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
2254 accel_clear_margins(vc, info, 0);
2255 update_screen(vc->vc_num);
2258 if (old_data && (--REFCOUNT(old_data) == 0))
2259 kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
2263 static int fbcon_copy_font(struct vc_data *vc, int con)
2265 struct display *od = &fb_display[con];
2266 struct console_font *f = &vc->vc_font;
2268 if (od->fontdata == f->data)
2269 return 0; /* already the same font... */
2270 return fbcon_do_set_font(vc, f->width, f->height, od->fontdata, od->userfont);
2274 * User asked to set font; we are guaranteed that
2275 * a) width and height are in range 1..32
2276 * b) charcount does not exceed 512
2279 static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags)
2281 unsigned charcount = font->charcount;
2282 int w = font->width;
2283 int h = font->height;
2286 u8 *new_data, *data = font->data, *p;
2288 if (charcount != 256 && charcount != 512)
2299 new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER);
2304 new_data += FONT_EXTRA_WORDS * sizeof(int);
2305 FNTSIZE(new_data) = size;
2306 FNTCHARCNT(new_data) = charcount;
2307 REFCOUNT(new_data) = 0; /* usage counter */
2310 for (i = 0; i < charcount; i++) {
2315 } else if (w <= 16) {
2317 for (i = 0; i < charcount; i++) {
2322 } else if (w <= 24) {
2323 for (i = 0; i < charcount; i++) {
2325 for (j = 0; j < h; j++) {
2331 data += 3 * (32 - h);
2335 for (i = 0; i < charcount; i++) {
2341 /* we can do it in u32 chunks because of charcount is 256 or 512, so
2342 font length must be multiple of 256, at least. And 256 is multiple
2345 while (p > new_data) {
2346 p = (u8 *)((u32 *)p - 1);
2349 FNTSUM(new_data) = k;
2350 /* Check if the same font is on some other console already */
2351 for (i = 0; i < MAX_NR_CONSOLES; i++) {
2352 struct vc_data *tmp = vc_cons[i].d;
2354 if (fb_display[i].userfont &&
2355 fb_display[i].fontdata &&
2356 FNTSUM(fb_display[i].fontdata) == k &&
2357 FNTSIZE(fb_display[i].fontdata) == size &&
2358 tmp->vc_font.width == w &&
2359 !memcmp(fb_display[i].fontdata, new_data, size)) {
2360 kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
2361 new_data = fb_display[i].fontdata;
2365 return fbcon_do_set_font(vc, font->width, font->height, new_data, 1);
2368 static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name)
2370 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2371 struct font_desc *f;
2374 f = get_default_font(info->var.xres, info->var.yres);
2375 else if (!(f = find_font(name)))
2378 font->width = f->width;
2379 font->height = f->height;
2380 return fbcon_do_set_font(vc, f->width, f->height, f->data, 0);
2383 static u16 palette_red[16];
2384 static u16 palette_green[16];
2385 static u16 palette_blue[16];
2387 static struct fb_cmap palette_cmap = {
2388 0, 16, palette_red, palette_green, palette_blue, NULL
2391 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table)
2393 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2397 if (!vc->vc_can_do_color
2398 || (!info->fbops->fb_blank && console_blanked))
2400 for (i = j = 0; i < 16; i++) {
2402 val = vc->vc_palette[j++];
2403 palette_red[k] = (val << 8) | val;
2404 val = vc->vc_palette[j++];
2405 palette_green[k] = (val << 8) | val;
2406 val = vc->vc_palette[j++];
2407 palette_blue[k] = (val << 8) | val;
2409 if (info->var.bits_per_pixel <= 4)
2410 palette_cmap.len = 1 << info->var.bits_per_pixel;
2412 palette_cmap.len = 16;
2413 palette_cmap.start = 0;
2414 return fb_set_cmap(&palette_cmap, info);
2417 static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
2422 if (vc->vc_num != fg_console || !softback_lines)
2423 return (u16 *) (vc->vc_origin + offset);
2424 line = offset / vc->vc_size_row;
2425 if (line >= softback_lines)
2426 return (u16 *) (vc->vc_origin + offset -
2427 softback_lines * vc->vc_size_row);
2428 p = softback_curr + offset;
2429 if (p >= softback_end)
2430 p += softback_buf - softback_end;
2434 static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
2440 if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
2441 unsigned long offset = (pos - vc->vc_origin) / 2;
2443 x = offset % vc->vc_cols;
2444 y = offset / vc->vc_cols;
2445 if (vc->vc_num == fg_console)
2446 y += softback_lines;
2447 ret = pos + (vc->vc_cols - x) * 2;
2448 } else if (vc->vc_num == fg_console && softback_lines) {
2449 unsigned long offset = pos - softback_curr;
2451 if (pos < softback_curr)
2452 offset += softback_end - softback_buf;
2454 x = offset % vc->vc_cols;
2455 y = offset / vc->vc_cols;
2456 ret = pos + (vc->vc_cols - x) * 2;
2457 if (ret == softback_end)
2459 if (ret == softback_in)
2460 ret = vc->vc_origin;
2462 /* Should not happen */
2464 ret = vc->vc_origin;
2473 /* As we might be inside of softback, we may work with non-contiguous buffer,
2474 that's why we have to use a separate routine. */
2475 static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
2478 u16 a = scr_readw(p);
2479 if (!vc->vc_can_do_color)
2481 else if (vc->vc_hi_font_mask == 0x100)
2482 a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
2483 (((a) & 0x0e00) << 4);
2485 a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
2486 (((a) & 0x0700) << 4);
2488 if (p == (u16 *) softback_end)
2489 p = (u16 *) softback_buf;
2490 if (p == (u16 *) softback_in)
2491 p = (u16 *) vc->vc_origin;
2495 static int fbcon_scrolldelta(struct vc_data *vc, int lines)
2497 struct fb_info *info = registered_fb[(int) con2fb_map[fg_console]];
2498 struct display *p = &fb_display[fg_console];
2499 int offset, limit, scrollback_old;
2502 if (vc->vc_num != fg_console)
2504 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT || !lines)
2506 if (logo_shown >= 0) {
2507 struct vc_data *conp2 = vc_cons[logo_shown].d;
2509 if (conp2->vc_top == logo_lines
2510 && conp2->vc_bottom == conp2->vc_rows)
2512 if (logo_shown == vc->vc_num) {
2518 logo_lines * vc->vc_size_row;
2519 for (i = 0; i < logo_lines; i++) {
2520 if (p == softback_top)
2522 if (p == softback_buf)
2524 p -= vc->vc_size_row;
2525 q -= vc->vc_size_row;
2526 scr_memcpyw((u16 *) q, (u16 *) p,
2530 update_region(vc->vc_num, vc->vc_origin,
2531 logo_lines * vc->vc_cols);
2535 fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK);
2536 fbcon_redraw_softback(vc, p, lines);
2537 fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK);
2541 if (!scrollback_phys_max)
2544 scrollback_old = scrollback_current;
2545 scrollback_current -= lines;
2546 if (scrollback_current < 0)
2547 scrollback_current = 0;
2548 else if (scrollback_current > scrollback_max)
2549 scrollback_current = scrollback_max;
2550 if (scrollback_current == scrollback_old)
2553 if (!info->fbops->fb_blank &&
2554 (console_blanked || vt_cons[vc->vc_num]->vc_mode != KD_TEXT
2557 fbcon_cursor(vc, CM_ERASE);
2559 offset = p->yscroll - scrollback_current;
2561 switch (p->scrollmode) {
2562 case SCROLL_WRAP_MOVE:
2563 info->var.vmode |= FB_VMODE_YWRAP;
2565 case SCROLL_PAN_MOVE:
2566 case SCROLL_PAN_REDRAW:
2567 limit -= vc->vc_rows;
2568 info->var.vmode &= ~FB_VMODE_YWRAP;
2573 else if (offset >= limit)
2575 info->var.xoffset = 0;
2576 info->var.yoffset = offset * vc->vc_font.height;
2577 update_var(vc->vc_num, info);
2578 if (!scrollback_current)
2579 fbcon_cursor(vc, CM_DRAW);
2583 static int fbcon_set_origin(struct vc_data *vc)
2585 if (softback_lines && !console_blanked)
2586 fbcon_scrolldelta(vc, softback_lines);
2590 static void fbcon_suspended(struct fb_info *info)
2592 /* Clear cursor, restore saved data */
2593 info->cursor.enable = 0;
2594 info->fbops->fb_cursor(info, &info->cursor);
2597 static void fbcon_resumed(struct fb_info *info)
2601 if (info->currcon < 0)
2603 vc = vc_cons[info->currcon].d;
2605 update_screen(vc->vc_num);
2608 static void fbcon_modechanged(struct fb_info *info)
2610 struct vc_data *vc = vc_cons[info->currcon].d;
2614 if (info->currcon < 0 || vt_cons[info->currcon]->vc_mode !=
2617 p = &fb_display[vc->vc_num];
2619 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
2620 vc->vc_can_do_color = info->var.bits_per_pixel != 1;
2621 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
2623 if (CON_IS_VISIBLE(vc)) {
2624 cols = info->var.xres / vc->vc_font.width;
2625 rows = info->var.yres / vc->vc_font.height;
2626 vc_resize(vc->vc_num, cols, rows);
2627 updatescrollmode(p, info, vc);
2629 scrollback_current = 0;
2630 update_var(vc->vc_num, info);
2631 fbcon_set_palette(vc, color_table);
2632 update_screen(vc->vc_num);
2634 int l = fbcon_softback_size / vc->vc_size_row;
2636 softback_end = softback_buf + l * vc->vc_size_row;
2638 /* Smaller scrollback makes no sense, and 0
2639 would screw the operation totally */
2646 static int fbcon_event_notify(struct notifier_block *self,
2647 unsigned long action, void *data)
2649 struct fb_info *info = (struct fb_info *) data;
2652 case FB_EVENT_SUSPEND:
2653 fbcon_suspended(info);
2655 case FB_EVENT_RESUME:
2656 fbcon_resumed(info);
2658 case FB_EVENT_MODE_CHANGE:
2659 fbcon_modechanged(info);
2667 * The console `switch' structure for the frame buffer based console
2670 const struct consw fb_con = {
2671 .owner = THIS_MODULE,
2672 .con_startup = fbcon_startup,
2673 .con_init = fbcon_init,
2674 .con_deinit = fbcon_deinit,
2675 .con_clear = fbcon_clear,
2676 .con_putc = fbcon_putc,
2677 .con_putcs = fbcon_putcs,
2678 .con_cursor = fbcon_cursor,
2679 .con_scroll = fbcon_scroll,
2680 .con_bmove = fbcon_bmove,
2681 .con_switch = fbcon_switch,
2682 .con_blank = fbcon_blank,
2683 .con_font_set = fbcon_set_font,
2684 .con_font_get = fbcon_get_font,
2685 .con_font_default = fbcon_set_def_font,
2686 .con_font_copy = fbcon_copy_font,
2687 .con_set_palette = fbcon_set_palette,
2688 .con_scrolldelta = fbcon_scrolldelta,
2689 .con_set_origin = fbcon_set_origin,
2690 .con_invert_region = fbcon_invert_region,
2691 .con_screen_pos = fbcon_screen_pos,
2692 .con_getxy = fbcon_getxy,
2693 .con_resize = fbcon_resize,
2696 static struct notifier_block fbcon_event_notifier = {
2697 .notifier_call = fbcon_event_notify,
2699 static int fbcon_event_notifier_registered;
2701 /* can't be __init as it can be called by set_con2fb_map() later */
2702 int fb_console_init(void)
2706 for (i = 0; i < MAX_NR_CONSOLES; i++)
2709 if (!num_registered_fb)
2711 if (info_idx == -1) {
2712 for (i = 0; i < FB_MAX; i++) {
2713 if (registered_fb[i] != NULL) {
2719 for (i = first_fb_vc; i <= last_fb_vc; i++)
2720 con2fb_map[i] = info_idx;
2721 #ifdef CONFIG_BOOTSPLASH
2725 err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
2728 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2733 acquire_console_sem();
2734 if (!fbcon_event_notifier_registered) {
2735 fb_register_client(&fbcon_event_notifier);
2736 fbcon_event_notifier_registered = 1;
2738 release_console_sem();
2744 void __exit fb_console_exit(void)
2746 acquire_console_sem();
2747 if (fbcon_event_notifier_registered) {
2748 fb_unregister_client(&fbcon_event_notifier);
2749 fbcon_event_notifier_registered = 0;
2751 release_console_sem();
2752 give_up_console(&fb_con);
2755 module_init(fb_console_init);
2756 module_exit(fb_console_exit);
2761 * Visible symbols for modules
2764 EXPORT_SYMBOL(fb_display);
2765 EXPORT_SYMBOL(fb_con);
2767 MODULE_LICENSE("GPL");