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>
98 # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
100 # define DPRINTK(fmt, args...)
103 struct display fb_display[MAX_NR_CONSOLES];
104 signed char con2fb_map[MAX_NR_CONSOLES];
105 static int logo_height;
106 static int logo_lines;
107 static int logo_shown = -1;
108 /* Software scrollback */
109 int fbcon_softback_size = 32768;
110 static unsigned long softback_buf, softback_curr;
111 static unsigned long softback_in;
112 static unsigned long softback_top, softback_end;
113 static int softback_lines;
114 /* console mappings */
115 static int first_fb_vc;
116 static int last_fb_vc = MAX_NR_CONSOLES - 1;
117 static int fbcon_is_default = 1;
119 static char fontname[40];
121 /* current fb_info */
122 static int info_idx = -1;
124 #define REFCOUNT(fd) (((int *)(fd))[-1])
125 #define FNTSIZE(fd) (((int *)(fd))[-2])
126 #define FNTCHARCNT(fd) (((int *)(fd))[-3])
127 #define FNTSUM(fd) (((int *)(fd))[-4])
128 #define FONT_EXTRA_WORDS 4
130 #define CM_SOFTBACK (8)
132 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
134 static void fbcon_free_font(struct display *);
135 static int fbcon_set_origin(struct vc_data *);
137 #define CURSOR_DRAW_DELAY (1)
139 /* # VBL ints between cursor state changes */
140 #define ARM_CURSOR_BLINK_RATE (10)
141 #define ATARI_CURSOR_BLINK_RATE (42)
142 #define MAC_CURSOR_BLINK_RATE (32)
143 #define DEFAULT_CURSOR_BLINK_RATE (20)
145 static int vbl_cursor_cnt;
147 #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1)
150 * Interface used by the world
153 static const char *fbcon_startup(void);
154 static void fbcon_init(struct vc_data *vc, int init);
155 static void fbcon_deinit(struct vc_data *vc);
156 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
158 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos);
159 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
160 int count, int ypos, int xpos);
161 static void fbcon_cursor(struct vc_data *vc, int mode);
162 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
164 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
165 int height, int width);
166 static int fbcon_switch(struct vc_data *vc);
167 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch);
168 static int fbcon_font_op(struct vc_data *vc, struct console_font_op *op);
169 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table);
170 static int fbcon_scrolldelta(struct vc_data *vc, int lines);
171 void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
178 static __inline__ int real_y(struct display *p, int ypos);
179 static __inline__ void ywrap_up(struct vc_data *vc, int count);
180 static __inline__ void ywrap_down(struct vc_data *vc, int count);
181 static __inline__ void ypan_up(struct vc_data *vc, int count);
182 static __inline__ void ypan_down(struct vc_data *vc, int count);
183 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
184 int dy, int dx, int height, int width, u_int y_break);
185 static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc);
186 static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
187 int line, int count, int dy);
191 * On the Macintoy, there may or may not be a working VBL int. We need to probe
193 static int vbl_detected;
195 static irqreturn_t fb_vbl_detect(int irq, void *dummy, struct pt_regs *fp)
202 static void fb_flashcursor(void *private)
204 struct fb_info *info = (struct fb_info *) private;
205 struct vc_data *vc = NULL;
207 if (info->currcon != -1)
208 vc = vc_cons[info->currcon].d;
210 if (info->state != FBINFO_STATE_RUNNING ||
211 info->cursor.rop == ROP_COPY || !vc || !CON_IS_VISIBLE(vc)
212 || registered_fb[(int) con2fb_map[vc->vc_num]] != info)
214 acquire_console_sem();
215 info->cursor.enable ^= 1;
216 info->fbops->fb_cursor(info, &info->cursor);
217 release_console_sem();
220 #if (defined(__arm__) && defined(IRQ_VSYNCPULSE)) || defined(CONFIG_ATARI) || defined(CONFIG_MAC)
221 static int cursor_blink_rate;
222 static irqreturn_t fb_vbl_handler(int irq, void *dev_id, struct pt_regs *fp)
224 struct fb_info *info = dev_id;
226 if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
227 schedule_work(&info->queue);
228 vbl_cursor_cnt = cursor_blink_rate;
234 static void cursor_timer_handler(unsigned long dev_addr)
236 struct fb_info *info = (struct fb_info *) dev_addr;
238 schedule_work(&info->queue);
239 mod_timer(&info->cursor_timer, jiffies + HZ/5);
242 int __init fb_console_setup(char *this_opt)
247 if (!this_opt || !*this_opt)
250 while ((options = strsep(&this_opt, ",")) != NULL) {
251 if (!strncmp(options, "font:", 5))
252 strcpy(fontname, options + 5);
254 if (!strncmp(options, "scrollback:", 11)) {
257 fbcon_softback_size = simple_strtoul(options, &options, 0);
258 if (*options == 'k' || *options == 'K') {
259 fbcon_softback_size *= 1024;
269 if (!strncmp(options, "map:", 4)) {
272 for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
275 con2fb_map[i] = (options[j++]-'0') % FB_MAX;
280 if (!strncmp(options, "vc:", 3)) {
283 first_fb_vc = simple_strtoul(options, &options, 10) - 1;
286 if (*options++ == '-')
287 last_fb_vc = simple_strtoul(options, &options, 10) - 1;
288 fbcon_is_default = 0;
294 __setup("fbcon=", fb_console_setup);
296 static int search_fb_in_map(int idx)
300 for (i = 0; i < MAX_NR_CONSOLES; i++) {
301 if (con2fb_map[i] == idx)
307 static int search_for_mapped_con(void)
311 for (i = 0; i < MAX_NR_CONSOLES; i++) {
312 if (con2fb_map[i] != -1)
319 * set_con2fb_map - map console to frame buffer device
320 * @unit: virtual console number to map
321 * @newidx: frame buffer index to map virtual console to
323 * Maps a virtual console @unit to a frame buffer device
326 int set_con2fb_map(int unit, int newidx)
328 struct vc_data *vc = vc_cons[unit].d;
329 int oldidx = con2fb_map[unit];
330 struct fb_info *info = registered_fb[newidx];
331 struct fb_info *oldinfo = NULL;
334 if (oldidx == newidx)
340 if (!search_for_mapped_con()) {
347 oldinfo = registered_fb[oldidx];
349 found = search_fb_in_map(newidx);
351 acquire_console_sem();
352 con2fb_map[unit] = newidx;
354 if (!try_module_get(info->fbops->owner)) {
355 con2fb_map[unit] = oldidx;
356 release_console_sem();
359 if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
360 module_put(info->fbops->owner);
361 con2fb_map[unit] = oldidx;
362 release_console_sem();
368 * If old fb is not mapped to any of the consoles,
369 * fbcon should release it.
371 if (oldinfo && !search_fb_in_map(oldidx)) {
372 if (oldinfo->fbops->fb_release &&
373 oldinfo->fbops->fb_release(oldinfo, 0)) {
374 con2fb_map[unit] = oldidx;
375 if (!found && info->fbops->fb_release)
376 info->fbops->fb_release(info, 0);
378 module_put(info->fbops->owner);
379 release_console_sem();
382 if (oldinfo->queue.func == fb_flashcursor)
383 del_timer_sync(&oldinfo->cursor_timer);
384 module_put(oldinfo->fbops->owner);
388 if (!info->queue.func || info->queue.func == fb_flashcursor) {
389 if (!info->queue.func)
390 INIT_WORK(&info->queue, fb_flashcursor, info);
392 init_timer(&info->cursor_timer);
393 info->cursor_timer.function = cursor_timer_handler;
394 info->cursor_timer.expires = jiffies + HZ / 5;
395 info->cursor_timer.data = (unsigned long ) info;
396 add_timer(&info->cursor_timer);
399 if (info->fbops->fb_set_par)
400 info->fbops->fb_set_par(info);
401 fbcon_set_disp(info, vc);
402 release_console_sem();
407 * Accelerated handlers.
409 void accel_bmove(struct vc_data *vc, struct fb_info *info, int sy,
410 int sx, int dy, int dx, int height, int width)
412 struct fb_copyarea area;
414 area.sx = sx * vc->vc_font.width;
415 area.sy = sy * vc->vc_font.height;
416 area.dx = dx * vc->vc_font.width;
417 area.dy = dy * vc->vc_font.height;
418 area.height = height * vc->vc_font.height;
419 area.width = width * vc->vc_font.width;
421 info->fbops->fb_copyarea(info, &area);
424 void accel_clear(struct vc_data *vc, struct fb_info *info, int sy,
425 int sx, int height, int width)
427 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
428 struct fb_fillrect region;
430 region.color = attr_bgcol_ec(bgshift, vc);
431 region.dx = sx * vc->vc_font.width;
432 region.dy = sy * vc->vc_font.height;
433 region.width = width * vc->vc_font.width;
434 region.height = height * vc->vc_font.height;
435 region.rop = ROP_COPY;
437 info->fbops->fb_fillrect(info, ®ion);
440 void accel_putcs(struct vc_data *vc, struct fb_info *info,
441 const unsigned short *s, int count, int yy, int xx)
443 void (*move_unaligned)(struct fb_info *info, struct fb_pixmap *buf,
444 u8 *dst, u32 d_pitch, u8 *src, u32 idx,
445 u32 height, u32 shift_high, u32 shift_low,
447 void (*move_aligned)(struct fb_info *info, struct fb_pixmap *buf,
448 u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch,
450 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
451 unsigned int width = (vc->vc_font.width + 7) >> 3;
452 unsigned int cellsize = vc->vc_font.height * width;
453 unsigned int maxcnt = info->pixmap.size/cellsize;
454 unsigned int scan_align = info->pixmap.scan_align - 1;
455 unsigned int buf_align = info->pixmap.buf_align - 1;
456 unsigned int shift_low = 0, mod = vc->vc_font.width % 8;
457 unsigned int shift_high = 8, pitch, cnt, size, k;
458 unsigned int idx = vc->vc_font.width >> 3;
459 struct fb_image image;
462 image.fg_color = attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8,
464 image.bg_color = attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12,
466 image.dx = xx * vc->vc_font.width;
467 image.dy = yy * vc->vc_font.height;
468 image.height = vc->vc_font.height;
471 if (info->pixmap.outbuf && info->pixmap.inbuf) {
472 move_aligned = fb_iomove_buf_aligned;
473 move_unaligned = fb_iomove_buf_unaligned;
475 move_aligned = fb_sysmove_buf_aligned;
476 move_unaligned = fb_sysmove_buf_unaligned;
484 image.width = vc->vc_font.width * cnt;
485 pitch = ((image.width + 7) >> 3) + scan_align;
486 pitch &= ~scan_align;
487 size = pitch * image.height + buf_align;
489 dst = fb_get_buffer_offset(info, &info->pixmap, size);
493 src = vc->vc_font.data + (scr_readw(s++)&
495 move_unaligned(info, &info->pixmap, dst, pitch,
496 src, idx, image.height,
497 shift_high, shift_low, mod);
499 dst += (shift_low >= 8) ? width : width - 1;
501 shift_high = 8 - shift_low;
505 src = vc->vc_font.data + (scr_readw(s++)&
507 move_aligned(info, &info->pixmap, dst, pitch,
508 src, idx, image.height);
512 info->fbops->fb_imageblit(info, &image);
513 image.dx += cnt * vc->vc_font.width;
518 void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
521 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
522 unsigned int cw = vc->vc_font.width;
523 unsigned int ch = vc->vc_font.height;
524 unsigned int rw = info->var.xres - (vc->vc_cols*cw);
525 unsigned int bh = info->var.yres - (vc->vc_rows*ch);
526 unsigned int rs = info->var.xres - rw;
527 unsigned int bs = info->var.yres - bh;
528 struct fb_fillrect region;
530 region.color = attr_bgcol_ec(bgshift, vc);
531 region.rop = ROP_COPY;
533 if (rw && !bottom_only) {
534 region.dx = info->var.xoffset + rs;
537 region.height = info->var.yres_virtual;
538 info->fbops->fb_fillrect(info, ®ion);
542 region.dx = info->var.xoffset;
543 region.dy = info->var.yoffset + bs;
546 info->fbops->fb_fillrect(info, ®ion);
551 * Low Level Operations
553 /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */
554 static const char *fbcon_startup(void)
556 const char *display_desc = "frame buffer device";
557 struct display *p = &fb_display[fg_console];
558 struct vc_data *vc = vc_cons[fg_console].d;
559 struct font_desc *font = NULL;
560 struct module *owner;
561 struct fb_info *info = NULL;
567 * If num_registered_fb is zero, this is a call for the dummy part.
568 * The frame buffer devices weren't initialized yet.
570 if (!num_registered_fb || info_idx == -1)
573 * Instead of blindly using registered_fb[0], we use info_idx, set by
576 info = registered_fb[info_idx];
581 owner = info->fbops->owner;
582 if (!try_module_get(owner))
584 if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
588 if (info->fix.type != FB_TYPE_TEXT) {
589 if (fbcon_softback_size) {
593 kmalloc(fbcon_softback_size,
596 fbcon_softback_size = 0;
602 kfree((void *) softback_buf);
608 softback_in = softback_top = softback_curr =
613 /* Setup default font */
615 if (!fontname[0] || !(font = find_font(fontname)))
616 font = get_default_font(info->var.xres,
618 vc->vc_font.width = font->width;
619 vc->vc_font.height = font->height;
620 vc->vc_font.data = p->fontdata = font->data;
621 vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */
624 cols = info->var.xres / vc->vc_font.width;
625 rows = info->var.yres / vc->vc_font.height;
626 vc_resize(vc->vc_num, cols, rows);
628 DPRINTK("mode: %s\n", info->fix.id);
629 DPRINTK("visual: %d\n", info->fix.visual);
630 DPRINTK("res: %dx%d-%d\n", info->var.xres,
632 info->var.bits_per_pixel);
633 con_set_default_unimap(vc->vc_num);
637 cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
639 request_irq(IRQ_AUTO_4, fb_vbl_handler,
640 IRQ_TYPE_PRIO, "framebuffer vbl",
643 #endif /* CONFIG_ATARI */
647 * On a Macintoy, the VBL interrupt may or may not be active.
648 * As interrupt based cursor is more reliable and race free, we
649 * probe for VBL interrupts.
654 * Probe for VBL: set temp. handler ...
656 irqres = request_irq(IRQ_MAC_VBL, fb_vbl_detect, 0,
657 "framebuffer vbl", info);
661 * ... and spin for 20 ms ...
663 while (!vbl_detected && ++ct < 1000)
668 ("fbcon_startup: No VBL detected, using timer based cursor.\n");
670 free_irq(IRQ_MAC_VBL, fb_vbl_detect);
674 * interrupt based cursor ok
676 cursor_blink_rate = MAC_CURSOR_BLINK_RATE;
678 request_irq(IRQ_MAC_VBL, fb_vbl_handler, 0,
679 "framebuffer vbl", info);
682 * VBL not detected: fall through, use timer based cursor
687 #endif /* CONFIG_MAC */
689 #if defined(__arm__) && defined(IRQ_VSYNCPULSE)
690 cursor_blink_rate = ARM_CURSOR_BLINK_RATE;
691 irqres = request_irq(IRQ_VSYNCPULSE, fb_vbl_handler, SA_SHIRQ,
692 "framebuffer vbl", info);
694 /* Initialize the work queue. If the driver provides its
695 * own work queue this means it will use something besides
696 * default timer to flash the cursor. */
697 if (!info->queue.func) {
698 INIT_WORK(&info->queue, fb_flashcursor, info);
700 init_timer(&info->cursor_timer);
701 info->cursor_timer.function = cursor_timer_handler;
702 info->cursor_timer.expires = jiffies + HZ / 5;
703 info->cursor_timer.data = (unsigned long ) info;
704 add_timer(&info->cursor_timer);
709 static void fbcon_init(struct vc_data *vc, int init)
711 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
712 struct vc_data **default_mode = vc->vc_display_fg;
713 struct display *t, *p = &fb_display[vc->vc_num];
714 int display_fg = (*default_mode)->vc_num;
715 int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;
716 unsigned short *save = NULL, *r, *q;
717 int cap = info->flags;
719 if (info_idx == -1 || info == NULL)
721 if (vc->vc_num != display_fg || (info->flags & FBINFO_MODULE) ||
722 (info->fix.type == FB_TYPE_TEXT))
725 info->var.xoffset = info->var.yoffset = p->yscroll = 0; /* reset wrap/pan */
727 /* If we are not the first console on this
728 fb, copy the font from that console */
729 t = &fb_display[display_fg];
730 if (!vc->vc_font.data) {
731 vc->vc_font.data = p->fontdata = t->fontdata;
732 vc->vc_font.width = (*default_mode)->vc_font.width;
733 vc->vc_font.height = (*default_mode)->vc_font.height;
734 p->userfont = t->userfont;
736 REFCOUNT(p->fontdata)++;
737 con_copy_unimap(vc->vc_num, display_fg);
740 charcnt = FNTCHARCNT(p->fontdata);
741 vc->vc_can_do_color = info->var.bits_per_pixel != 1;
742 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
743 if (charcnt == 256) {
744 vc->vc_hi_font_mask = 0;
746 vc->vc_hi_font_mask = 0x100;
747 if (vc->vc_can_do_color)
748 vc->vc_complement_mask <<= 1;
752 new_cols = info->var.xres / vc->vc_font.width;
753 new_rows = info->var.yres / vc->vc_font.height;
754 vc_resize(vc->vc_num, new_cols, new_rows);
756 * We must always set the mode. The mode of the previous console
757 * driver could be in the same resolution but we are using different
758 * hardware so we have to initialize the hardware.
760 * We need to do it in fbcon_init() to prevent screen corruption.
762 if (CON_IS_VISIBLE(vc) && info->fbops->fb_set_par)
763 info->fbops->fb_set_par(info);
766 if ((cap & FBINFO_HWACCEL_COPYAREA) &&
767 !(cap & FBINFO_HWACCEL_DISABLED))
768 p->scrollmode = SCROLL_MOVE;
769 else /* default to something safe */
770 p->scrollmode = SCROLL_REDRAW;
773 * ++guenther: console.c:vc_allocate() relies on initializing
774 * vc_{cols,rows}, but we must not set those if we are only
775 * resizing the console.
778 vc->vc_cols = new_cols;
779 vc->vc_rows = new_rows;
783 /* Need to make room for the logo */
787 logo_height = fb_prepare_logo(info);
788 logo_lines = (logo_height + vc->vc_font.height - 1) /
790 q = (unsigned short *) (vc->vc_origin +
791 vc->vc_size_row * rows);
792 step = logo_lines * cols;
793 for (r = q - logo_lines * cols; r < q; r++)
794 if (scr_readw(r) != vc->vc_video_erase_char)
796 if (r != q && new_rows >= rows + logo_lines) {
797 save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL);
799 int i = cols < new_cols ? cols : new_cols;
800 scr_memsetw(save, vc->vc_video_erase_char,
801 logo_lines * new_cols * 2);
803 for (cnt = 0; cnt < logo_lines; cnt++, r += i)
804 scr_memcpyw(save + cnt * new_cols, r, 2 * i);
809 /* We can scroll screen down */
811 for (cnt = rows - logo_lines; cnt > 0; cnt--) {
812 scr_memcpyw(r + step, r, vc->vc_size_row);
816 vc->vc_y += logo_lines;
817 vc->vc_pos += logo_lines * vc->vc_size_row;
820 scr_memsetw((unsigned short *) vc->vc_origin,
821 vc->vc_video_erase_char,
822 vc->vc_size_row * logo_lines);
824 if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
825 accel_clear_margins(vc, info, 0);
826 update_screen(vc->vc_num);
829 q = (unsigned short *) (vc->vc_origin +
832 scr_memcpyw(q, save, logo_lines * new_cols * 2);
833 vc->vc_y += logo_lines;
834 vc->vc_pos += logo_lines * vc->vc_size_row;
837 if (logo_lines > vc->vc_bottom) {
840 "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
843 vc->vc_top = logo_lines;
847 if (vc->vc_num == display_fg && softback_buf) {
848 int l = fbcon_softback_size / vc->vc_size_row;
850 softback_end = softback_buf + l * vc->vc_size_row;
852 /* Smaller scrollback makes no sense, and 0 would screw
853 the operation totally */
859 static void fbcon_deinit(struct vc_data *vc)
861 struct display *p = &fb_display[vc->vc_num];
868 /* ====================================================================== */
870 /* fbcon_XXX routines - interface used by the world
872 * This system is now divided into two levels because of complications
873 * caused by hardware scrolling. Top level functions:
875 * fbcon_bmove(), fbcon_clear(), fbcon_putc()
877 * handles y values in range [0, scr_height-1] that correspond to real
878 * screen positions. y_wrap shift means that first line of bitmap may be
879 * anywhere on this display. These functions convert lineoffsets to
880 * bitmap offsets and deal with the wrap-around case by splitting blits.
882 * fbcon_bmove_physical_8() -- These functions fast implementations
883 * fbcon_clear_physical_8() -- of original fbcon_XXX fns.
884 * fbcon_putc_physical_8() -- (font width != 8) may be added later
888 * At the moment fbcon_putc() cannot blit across vertical wrap boundary
889 * Implies should only really hardware scroll in rows. Only reason for
890 * restriction is simplicity & efficiency at the moment.
893 static __inline__ int real_y(struct display *p, int ypos)
898 return ypos < rows ? ypos : ypos - rows;
902 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
905 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
907 struct display *p = &fb_display[vc->vc_num];
910 if (!info->fbops->fb_blank && console_blanked)
912 if (info->state != FBINFO_STATE_RUNNING)
915 if (!height || !width)
918 /* Split blits that cross physical y_wrap boundary */
920 y_break = p->vrows - p->yscroll;
921 if (sy < y_break && sy + height - 1 >= y_break) {
922 u_int b = y_break - sy;
923 accel_clear(vc, info, real_y(p, sy), sx, b, width);
924 accel_clear(vc, info, real_y(p, sy + b), sx, height - b,
927 accel_clear(vc, info, real_y(p, sy), sx, height, width);
930 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
932 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
933 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
934 unsigned int scan_align = info->pixmap.scan_align - 1;
935 unsigned int buf_align = info->pixmap.buf_align - 1;
936 unsigned int width = (vc->vc_font.width + 7) >> 3;
937 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
938 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
939 struct display *p = &fb_display[vc->vc_num];
940 unsigned int size, pitch;
941 struct fb_image image;
944 if (!info->fbops->fb_blank && console_blanked)
946 if (info->state != FBINFO_STATE_RUNNING)
949 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
952 image.dx = xpos * vc->vc_font.width;
953 image.dy = real_y(p, ypos) * vc->vc_font.height;
954 image.width = vc->vc_font.width;
955 image.height = vc->vc_font.height;
956 image.fg_color = attr_fgcol(fgshift, c);
957 image.bg_color = attr_bgcol(bgshift, c);
960 src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * width;
962 pitch = width + scan_align;
963 pitch &= ~scan_align;
964 size = pitch * vc->vc_font.height;
968 dst = fb_get_buffer_offset(info, &info->pixmap, size);
971 if (info->pixmap.outbuf)
972 fb_iomove_buf_aligned(info, &info->pixmap, dst, pitch, src, width, image.height);
974 fb_sysmove_buf_aligned(info, &info->pixmap, dst, pitch, src, width, image.height);
976 info->fbops->fb_imageblit(info, &image);
979 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
980 int count, int ypos, int xpos)
982 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
983 struct display *p = &fb_display[vc->vc_num];
985 if (!info->fbops->fb_blank && console_blanked)
987 if (info->state != FBINFO_STATE_RUNNING)
990 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
993 accel_putcs(vc, info, s, count, real_y(p, ypos), xpos);
996 static void fbcon_cursor(struct vc_data *vc, int mode)
998 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
999 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
1000 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
1001 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
1002 struct display *p = &fb_display[vc->vc_num];
1003 int w = (vc->vc_font.width + 7) >> 3, c;
1004 int y = real_y(p, vc->vc_y);
1005 struct fb_cursor cursor;
1007 if (mode & CM_SOFTBACK) {
1008 mode &= ~CM_SOFTBACK;
1009 if (softback_lines) {
1010 if (y + softback_lines >= vc->vc_rows)
1013 y += softback_lines;
1015 } else if (softback_lines)
1016 fbcon_set_origin(vc);
1018 c = scr_readw((u16 *) vc->vc_pos);
1020 cursor.image.data = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
1021 cursor.set = FB_CUR_SETCUR;
1022 cursor.image.depth = 1;
1026 if (info->cursor.rop == ROP_XOR) {
1027 info->cursor.enable = 0;
1028 info->cursor.rop = ROP_COPY;
1029 info->fbops->fb_cursor(info, &cursor);
1034 info->cursor.enable = 1;
1036 if (info->cursor.image.fg_color != attr_fgcol(fgshift, c) ||
1037 info->cursor.image.bg_color != attr_bgcol(bgshift, c)) {
1038 cursor.image.fg_color = attr_fgcol(fgshift, c);
1039 cursor.image.bg_color = attr_bgcol(bgshift, c);
1040 cursor.set |= FB_CUR_SETCMAP;
1043 if ((info->cursor.image.dx != (vc->vc_font.width * vc->vc_x)) ||
1044 (info->cursor.image.dy != (vc->vc_font.height * y))) {
1045 cursor.image.dx = vc->vc_font.width * vc->vc_x;
1046 cursor.image.dy = vc->vc_font.height * y;
1047 cursor.set |= FB_CUR_SETPOS;
1050 if (info->cursor.image.height != vc->vc_font.height ||
1051 info->cursor.image.width != vc->vc_font.width) {
1052 cursor.image.height = vc->vc_font.height;
1053 cursor.image.width = vc->vc_font.width;
1054 cursor.set |= FB_CUR_SETSIZE;
1057 if (info->cursor.hot.x || info->cursor.hot.y) {
1058 cursor.hot.x = cursor.hot.y = 0;
1059 cursor.set |= FB_CUR_SETHOT;
1062 if ((cursor.set & FB_CUR_SETSIZE) || ((vc->vc_cursor_type & 0x0f) != p->cursor_shape)
1063 || info->cursor.mask == NULL) {
1064 char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
1065 int cur_height, size, i = 0;
1069 if (info->cursor.mask)
1070 kfree(info->cursor.mask);
1071 info->cursor.mask = mask;
1073 p->cursor_shape = vc->vc_cursor_type & 0x0f;
1074 cursor.set |= FB_CUR_SETSHAPE;
1076 switch (vc->vc_cursor_type & 0x0f) {
1081 cur_height = (vc->vc_font.height < 10) ? 1 : 2;
1083 case CUR_LOWER_THIRD:
1084 cur_height = vc->vc_font.height/3;
1086 case CUR_LOWER_HALF:
1087 cur_height = vc->vc_font.height >> 1;
1089 case CUR_TWO_THIRDS:
1090 cur_height = (vc->vc_font.height << 1)/3;
1094 cur_height = vc->vc_font.height;
1097 size = (vc->vc_font.height - cur_height) * w;
1100 size = cur_height * w;
1104 info->cursor.rop = ROP_XOR;
1105 info->fbops->fb_cursor(info, &cursor);
1106 vbl_cursor_cnt = CURSOR_DRAW_DELAY;
1111 static int scrollback_phys_max = 0;
1112 static int scrollback_max = 0;
1113 static int scrollback_current = 0;
1115 int update_var(int con, struct fb_info *info)
1117 if (con == info->currcon)
1118 return fb_pan_display(info, &info->var);
1122 static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc)
1124 struct display *p = &fb_display[vc->vc_num], *t;
1125 struct vc_data **default_mode = vc->vc_display_fg;
1126 int display_fg = (*default_mode)->vc_num;
1127 int rows, cols, charcnt = 256;
1129 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
1130 t = &fb_display[display_fg];
1131 if (!vc->vc_font.data) {
1132 vc->vc_font.data = p->fontdata = t->fontdata;
1133 vc->vc_font.width = (*default_mode)->vc_font.width;
1134 vc->vc_font.height = (*default_mode)->vc_font.height;
1135 p->userfont = t->userfont;
1137 REFCOUNT(p->fontdata)++;
1138 con_copy_unimap(vc->vc_num, display_fg);
1141 charcnt = FNTCHARCNT(p->fontdata);
1143 vc->vc_can_do_color = info->var.bits_per_pixel != 1;
1144 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1145 if (charcnt == 256) {
1146 vc->vc_hi_font_mask = 0;
1148 vc->vc_hi_font_mask = 0x100;
1149 if (vc->vc_can_do_color)
1150 vc->vc_complement_mask <<= 1;
1152 cols = info->var.xres / vc->vc_font.width;
1153 rows = info->var.yres / vc->vc_font.height;
1154 vc_resize(vc->vc_num, cols, rows);
1155 if (CON_IS_VISIBLE(vc)) {
1156 update_screen(vc->vc_num);
1158 int l = fbcon_softback_size / vc->vc_size_row;
1161 softback_end = softback_buf + l *
1164 /* Smaller scrollback makes no sense, and 0
1165 would screw the operation totally */
1170 switch_screen(fg_console);
1173 static __inline__ void ywrap_up(struct vc_data *vc, int count)
1175 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1176 struct display *p = &fb_display[vc->vc_num];
1178 p->yscroll += count;
1179 if (p->yscroll >= p->vrows) /* Deal with wrap */
1180 p->yscroll -= p->vrows;
1181 info->var.xoffset = 0;
1182 info->var.yoffset = p->yscroll * vc->vc_font.height;
1183 info->var.vmode |= FB_VMODE_YWRAP;
1184 update_var(vc->vc_num, info);
1185 scrollback_max += count;
1186 if (scrollback_max > scrollback_phys_max)
1187 scrollback_max = scrollback_phys_max;
1188 scrollback_current = 0;
1191 static __inline__ void ywrap_down(struct vc_data *vc, int count)
1193 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1194 struct display *p = &fb_display[vc->vc_num];
1196 p->yscroll -= count;
1197 if (p->yscroll < 0) /* Deal with wrap */
1198 p->yscroll += p->vrows;
1199 info->var.xoffset = 0;
1200 info->var.yoffset = p->yscroll * vc->vc_font.height;
1201 info->var.vmode |= FB_VMODE_YWRAP;
1202 update_var(vc->vc_num, info);
1203 scrollback_max -= count;
1204 if (scrollback_max < 0)
1206 scrollback_current = 0;
1209 static __inline__ void ypan_up(struct vc_data *vc, int count)
1211 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1212 struct display *p = &fb_display[vc->vc_num];
1214 p->yscroll += count;
1215 if (p->yscroll > p->vrows - vc->vc_rows) {
1216 accel_bmove(vc, info, p->vrows - vc->vc_rows,
1217 0, 0, 0, vc->vc_rows, vc->vc_cols);
1218 p->yscroll -= p->vrows - vc->vc_rows;
1220 info->var.xoffset = 0;
1221 info->var.yoffset = p->yscroll * vc->vc_font.height;
1222 info->var.vmode &= ~FB_VMODE_YWRAP;
1223 update_var(vc->vc_num, info);
1224 accel_clear_margins(vc, info, 1);
1225 scrollback_max += count;
1226 if (scrollback_max > scrollback_phys_max)
1227 scrollback_max = scrollback_phys_max;
1228 scrollback_current = 0;
1231 static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
1233 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1234 struct display *p = &fb_display[vc->vc_num];
1237 p->yscroll += count;
1238 if (p->yscroll > p->vrows - vc->vc_rows) {
1239 p->yscroll -= p->vrows - vc->vc_rows;
1243 info->var.xoffset = 0;
1244 info->var.yoffset = p->yscroll * vc->vc_font.height;
1245 info->var.vmode &= ~FB_VMODE_YWRAP;
1247 fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
1248 update_var(vc->vc_num, info);
1249 accel_clear_margins(vc, info, 1);
1250 scrollback_max += count;
1251 if (scrollback_max > scrollback_phys_max)
1252 scrollback_max = scrollback_phys_max;
1253 scrollback_current = 0;
1256 static __inline__ void ypan_down(struct vc_data *vc, int count)
1258 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1259 struct display *p = &fb_display[vc->vc_num];
1261 p->yscroll -= count;
1262 if (p->yscroll < 0) {
1263 accel_bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
1264 0, vc->vc_rows, vc->vc_cols);
1265 p->yscroll += p->vrows - vc->vc_rows;
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 accel_clear_margins(vc, info, 1);
1272 scrollback_max -= count;
1273 if (scrollback_max < 0)
1275 scrollback_current = 0;
1278 static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
1280 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1281 struct display *p = &fb_display[vc->vc_num];
1284 p->yscroll -= count;
1285 if (p->yscroll < 0) {
1286 p->yscroll += p->vrows - vc->vc_rows;
1289 info->var.xoffset = 0;
1290 info->var.yoffset = p->yscroll * vc->vc_font.height;
1291 info->var.vmode &= ~FB_VMODE_YWRAP;
1293 fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
1294 update_var(vc->vc_num, info);
1295 accel_clear_margins(vc, info, 1);
1296 scrollback_max -= count;
1297 if (scrollback_max < 0)
1299 scrollback_current = 0;
1302 static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
1305 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1306 int count = vc->vc_rows;
1307 unsigned short *d, *s;
1311 d = (u16 *) softback_curr;
1312 if (d == (u16 *) softback_in)
1313 d = (u16 *) vc->vc_origin;
1314 n = softback_curr + delta * vc->vc_size_row;
1315 softback_lines -= delta;
1317 if (softback_curr < softback_top && n < softback_buf) {
1318 n += softback_end - softback_buf;
1319 if (n < softback_top) {
1321 (softback_top - n) / vc->vc_size_row;
1324 } else if (softback_curr >= softback_top
1325 && n < softback_top) {
1327 (softback_top - n) / vc->vc_size_row;
1331 if (softback_curr > softback_in && n >= softback_end) {
1332 n += softback_buf - softback_end;
1333 if (n > softback_in) {
1337 } else if (softback_curr <= softback_in && n > softback_in) {
1342 if (n == softback_curr)
1345 s = (u16 *) softback_curr;
1346 if (s == (u16 *) softback_in)
1347 s = (u16 *) vc->vc_origin;
1349 unsigned short *start;
1353 unsigned short attr = 1;
1356 le = advance_row(s, 1);
1359 if (attr != (c & 0xff00)) {
1362 accel_putcs(vc, info, start, s - start,
1363 real_y(p, line), x);
1368 if (c == scr_readw(d)) {
1370 accel_putcs(vc, info, start, s - start,
1371 real_y(p, line), x);
1383 accel_putcs(vc, info, start, s - start,
1384 real_y(p, line), x);
1386 if (d == (u16 *) softback_end)
1387 d = (u16 *) softback_buf;
1388 if (d == (u16 *) softback_in)
1389 d = (u16 *) vc->vc_origin;
1390 if (s == (u16 *) softback_end)
1391 s = (u16 *) softback_buf;
1392 if (s == (u16 *) softback_in)
1393 s = (u16 *) vc->vc_origin;
1397 static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
1398 int line, int count, int dy)
1400 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1401 unsigned short *s = (unsigned short *)
1402 (vc->vc_origin + vc->vc_size_row * line);
1405 unsigned short *start = s;
1406 unsigned short *le = advance_row(s, 1);
1409 unsigned short attr = 1;
1413 if (attr != (c & 0xff00)) {
1416 accel_putcs(vc, info, start, s - start,
1422 console_conditional_schedule();
1426 accel_putcs(vc, info, start, s - start,
1428 console_conditional_schedule();
1433 static void fbcon_redraw(struct vc_data *vc, struct display *p,
1434 int line, int count, int offset)
1436 unsigned short *d = (unsigned short *)
1437 (vc->vc_origin + vc->vc_size_row * line);
1438 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1439 unsigned short *s = d + offset;
1442 unsigned short *start = s;
1443 unsigned short *le = advance_row(s, 1);
1446 unsigned short attr = 1;
1450 if (attr != (c & 0xff00)) {
1453 accel_putcs(vc, info, start, s - start,
1454 real_y(p, line), x);
1459 if (c == scr_readw(d)) {
1461 accel_putcs(vc, info, start, s - start,
1462 real_y(p, line), x);
1471 console_conditional_schedule();
1476 accel_putcs(vc, info, start, s - start,
1477 real_y(p, line), x);
1478 console_conditional_schedule();
1483 /* NOTE: We subtract two lines from these pointers */
1484 s -= vc->vc_size_row;
1485 d -= vc->vc_size_row;
1490 static inline void fbcon_softback_note(struct vc_data *vc, int t,
1495 if (vc->vc_num != fg_console)
1497 p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
1500 scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row);
1502 p = advance_row(p, 1);
1503 softback_in += vc->vc_size_row;
1504 if (softback_in == softback_end)
1505 softback_in = softback_buf;
1506 if (softback_in == softback_top) {
1507 softback_top += vc->vc_size_row;
1508 if (softback_top == softback_end)
1509 softback_top = softback_buf;
1512 softback_curr = softback_in;
1515 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
1518 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1519 struct display *p = &fb_display[vc->vc_num];
1520 int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
1522 if (!info->fbops->fb_blank && console_blanked)
1525 if (!count || vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
1528 fbcon_cursor(vc, CM_ERASE);
1531 * ++Geert: Only use ywrap/ypan if the console is in text mode
1532 * ++Andrew: Only use ypan on hardware text mode when scrolling the
1533 * whole screen (prevents flicker).
1538 if (count > vc->vc_rows) /* Maximum realistic size */
1539 count = vc->vc_rows;
1541 fbcon_softback_note(vc, t, count);
1542 if (logo_shown >= 0)
1544 switch (p->scrollmode) {
1546 accel_bmove(vc, info, t + count, 0, t, 0,
1547 b - t - count, vc->vc_cols);
1548 accel_clear(vc, info, b - count, 0, count,
1552 case SCROLL_WRAP_MOVE:
1553 if (b - t - count > 3 * vc->vc_rows >> 2) {
1555 fbcon_bmove(vc, 0, 0, count, 0, t,
1557 ywrap_up(vc, count);
1558 if (vc->vc_rows - b > 0)
1559 fbcon_bmove(vc, b - count, 0, b, 0,
1562 } else if (info->flags & FBINFO_READS_FAST)
1563 fbcon_bmove(vc, t + count, 0, t, 0,
1564 b - t - count, vc->vc_cols);
1567 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1570 case SCROLL_PAN_REDRAW:
1571 if ((p->yscroll + count <=
1572 2 * (p->vrows - vc->vc_rows))
1573 && ((!scroll_partial && (b - t == vc->vc_rows))
1576 3 * vc->vc_rows >> 2)))) {
1578 fbcon_redraw_move(vc, p, 0, t, count);
1579 ypan_up_redraw(vc, t, count);
1580 if (vc->vc_rows - b > 0)
1581 fbcon_redraw_move(vc, p, b - count,
1582 vc->vc_rows - b, b);
1584 fbcon_redraw_move(vc, p, t + count, b - t - count, t);
1585 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1588 case SCROLL_PAN_MOVE:
1589 if ((p->yscroll + count <=
1590 2 * (p->vrows - vc->vc_rows))
1591 && ((!scroll_partial && (b - t == vc->vc_rows))
1594 3 * vc->vc_rows >> 2)))) {
1596 fbcon_bmove(vc, 0, 0, count, 0, t,
1599 if (vc->vc_rows - b > 0)
1600 fbcon_bmove(vc, b - count, 0, b, 0,
1603 } else if (info->flags & FBINFO_READS_FAST)
1604 fbcon_bmove(vc, t + count, 0, t, 0,
1605 b - t - count, vc->vc_cols);
1608 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1613 fbcon_redraw(vc, p, t, b - t - count,
1614 count * vc->vc_cols);
1615 accel_clear(vc, info, real_y(p, b - count), 0,
1616 count, vc->vc_cols);
1617 scr_memsetw((unsigned short *) (vc->vc_origin +
1620 vc->vc_video_erase_char,
1621 vc->vc_size_row * count);
1627 if (count > vc->vc_rows) /* Maximum realistic size */
1628 count = vc->vc_rows;
1629 switch (p->scrollmode) {
1631 accel_bmove(vc, info, t, 0, t + count, 0,
1632 b - t - count, vc->vc_cols);
1633 accel_clear(vc, info, t, 0, count, vc->vc_cols);
1636 case SCROLL_WRAP_MOVE:
1637 if (b - t - count > 3 * vc->vc_rows >> 2) {
1638 if (vc->vc_rows - b > 0)
1639 fbcon_bmove(vc, b, 0, b - count, 0,
1642 ywrap_down(vc, count);
1644 fbcon_bmove(vc, count, 0, 0, 0, t,
1646 } else if (info->flags & FBINFO_READS_FAST)
1647 fbcon_bmove(vc, t, 0, t + count, 0,
1648 b - t - count, vc->vc_cols);
1651 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1654 case SCROLL_PAN_MOVE:
1655 if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1656 && ((!scroll_partial && (b - t == vc->vc_rows))
1659 3 * vc->vc_rows >> 2)))) {
1660 if (vc->vc_rows - b > 0)
1661 fbcon_bmove(vc, b, 0, b - count, 0,
1664 ypan_down(vc, count);
1666 fbcon_bmove(vc, count, 0, 0, 0, t,
1668 } else if (info->flags & FBINFO_READS_FAST)
1669 fbcon_bmove(vc, t, 0, t + count, 0,
1670 b - t - count, vc->vc_cols);
1673 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1676 case SCROLL_PAN_REDRAW:
1677 if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1678 && ((!scroll_partial && (b - t == vc->vc_rows))
1681 3 * vc->vc_rows >> 2)))) {
1682 if (vc->vc_rows - b > 0)
1683 fbcon_redraw_move(vc, p, b, vc->vc_rows - b,
1685 ypan_down_redraw(vc, t, count);
1687 fbcon_redraw_move(vc, p, count, t, 0);
1689 fbcon_redraw_move(vc, p, t, b - t - count, t + count);
1690 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1695 fbcon_redraw(vc, p, b - 1, b - t - count,
1696 -count * vc->vc_cols);
1697 accel_clear(vc, info, real_y(p, t), 0, count,
1699 scr_memsetw((unsigned short *) (vc->vc_origin +
1702 vc->vc_video_erase_char,
1703 vc->vc_size_row * count);
1711 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
1712 int height, int width)
1714 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1715 struct display *p = &fb_display[vc->vc_num];
1717 if (!info->fbops->fb_blank && console_blanked)
1720 if (!width || !height)
1723 /* Split blits that cross physical y_wrap case.
1724 * Pathological case involves 4 blits, better to use recursive
1725 * code rather than unrolled case
1727 * Recursive invocations don't need to erase the cursor over and
1728 * over again, so we use fbcon_bmove_rec()
1730 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
1731 p->vrows - p->yscroll);
1734 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
1735 int dy, int dx, int height, int width, u_int y_break)
1737 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1740 if (sy < y_break && sy + height > y_break) {
1742 if (dy < sy) { /* Avoid trashing self */
1743 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1745 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1746 height - b, width, y_break);
1748 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1749 height - b, width, y_break);
1750 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1756 if (dy < y_break && dy + height > y_break) {
1758 if (dy < sy) { /* Avoid trashing self */
1759 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1761 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1762 height - b, width, y_break);
1764 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1765 height - b, width, y_break);
1766 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1771 accel_bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
1775 static __inline__ void updatescrollmode(struct display *p, struct fb_info *info,
1778 int fh = vc->vc_font.height;
1779 int cap = info->flags;
1780 int good_pan = (cap & FBINFO_HWACCEL_YPAN)
1781 && divides(info->fix.ypanstep, vc->vc_font.height)
1782 && info->var.yres_virtual > info->var.yres;
1783 int good_wrap = (cap & FBINFO_HWACCEL_YWRAP)
1784 && divides(info->fix.ywrapstep, vc->vc_font.height)
1785 && divides(vc->vc_font.height, info->var.yres_virtual);
1786 int reading_fast = cap & FBINFO_READS_FAST;
1787 int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) && !(cap & FBINFO_HWACCEL_DISABLED);
1788 int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) && !(cap & FBINFO_HWACCEL_DISABLED);
1790 p->vrows = info->var.yres_virtual/fh;
1791 if (info->var.yres > (fh * (vc->vc_rows + 1)))
1792 p->vrows -= (info->var.yres - (fh * vc->vc_rows)) / fh;
1793 if ((info->var.yres % fh) && (info->var.yres_virtual % fh <
1794 info->var.yres % fh))
1797 if (good_wrap || good_pan) {
1798 if (reading_fast || fast_copyarea)
1799 p->scrollmode = good_wrap ? SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
1801 p->scrollmode = good_wrap ? SCROLL_REDRAW :
1804 if (reading_fast || (fast_copyarea && !fast_imageblit))
1805 p->scrollmode = SCROLL_MOVE;
1807 p->scrollmode = SCROLL_REDRAW;
1811 static int fbcon_resize(struct vc_data *vc, unsigned int width,
1812 unsigned int height)
1814 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1815 struct display *p = &fb_display[vc->vc_num];
1816 struct fb_var_screeninfo var = info->var;
1817 int err; int x_diff, y_diff;
1818 int fw = vc->vc_font.width;
1819 int fh = vc->vc_font.height;
1821 var.xres = width * fw;
1822 var.yres = height * fh;
1823 x_diff = info->var.xres - var.xres;
1824 y_diff = info->var.yres - var.yres;
1825 if (x_diff < 0 || x_diff > fw || (y_diff < 0 || y_diff > fh)) {
1828 DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
1829 snprintf(mode, 40, "%ix%i", var.xres, var.yres);
1830 err = fb_find_mode(&var, info, mode, info->monspecs.modedb,
1831 info->monspecs.modedb_len, NULL,
1832 info->var.bits_per_pixel);
1833 if (!err || width > var.xres/fw || height > var.yres/fh)
1835 DPRINTK("resize now %ix%i\n", var.xres, var.yres);
1836 if (CON_IS_VISIBLE(vc)) {
1837 var.activate = FB_ACTIVATE_NOW |
1839 fb_set_var(info, &var);
1840 info->flags &= ~FBINFO_MISC_MODESWITCH;
1843 updatescrollmode(p, info, vc);
1847 static int fbcon_switch(struct vc_data *vc)
1849 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1850 struct display *p = &fb_display[vc->vc_num];
1854 int l = fbcon_softback_size / vc->vc_size_row;
1856 fbcon_set_origin(vc);
1857 softback_top = softback_curr = softback_in = softback_buf;
1861 softback_end = softback_buf + l * vc->vc_size_row;
1863 /* Smaller scrollback makes no sense, and 0 would screw
1864 the operation totally */
1868 if (logo_shown >= 0) {
1869 struct vc_data *conp2 = vc_cons[logo_shown].d;
1871 if (conp2->vc_top == logo_lines
1872 && conp2->vc_bottom == conp2->vc_rows)
1877 info->var.yoffset = p->yscroll = 0;
1880 * FIXME: If we have multiple fbdev's loaded, we need to
1881 * update all info->currcon. Perhaps, we can place this
1882 * in a centralized structure, but this might break some
1885 * info->currcon = vc->vc_num;
1887 for (i = 0; i < FB_MAX; i++) {
1888 if (registered_fb[i] != NULL)
1889 registered_fb[i]->currcon = vc->vc_num;
1892 fbcon_resize(vc, vc->vc_cols, vc->vc_rows);
1894 if (info->flags & FBINFO_MISC_MODESWITCH) {
1895 if (info->fbops->fb_set_par)
1896 info->fbops->fb_set_par(info);
1897 info->flags &= ~FBINFO_MISC_MODESWITCH;
1900 switch (p->scrollmode) {
1901 case SCROLL_WRAP_MOVE:
1902 scrollback_phys_max = p->vrows - vc->vc_rows;
1904 case SCROLL_PAN_MOVE:
1905 case SCROLL_PAN_REDRAW:
1906 scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
1907 if (scrollback_phys_max < 0)
1908 scrollback_phys_max = 0;
1911 scrollback_phys_max = 0;
1915 scrollback_current = 0;
1917 update_var(vc->vc_num, info);
1918 fbcon_set_palette(vc, color_table);
1920 if (vt_cons[vc->vc_num]->vc_mode == KD_TEXT)
1921 accel_clear_margins(vc, info, 0);
1922 if (logo_shown == -2) {
1923 logo_shown = fg_console;
1924 /* This is protected above by initmem_freed */
1926 update_region(fg_console,
1927 vc->vc_origin + vc->vc_size_row * vc->vc_top,
1928 vc->vc_size_row * (vc->vc_bottom -
1935 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
1937 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
1938 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1939 struct display *p = &fb_display[vc->vc_num];
1942 struct fb_var_screeninfo var = info->var;
1945 * HACK ALERT: Some hardware will require reinitializion at this stage,
1946 * others will require it to be done as late as possible.
1947 * For now, we differentiate this with the
1948 * FBINFO_MISC_MODESWITCHLATE bitflag. Worst case will be
1949 * hardware that requires it here and another one later.
1950 * A definitive solution may require fixing X or the VT
1953 if (info->flags & FBINFO_MISC_MODESWITCHLATE)
1954 info->flags |= FBINFO_MISC_MODESWITCH;
1957 fbcon_cursor(vc, CM_ERASE);
1961 if (!(info->flags & FBINFO_MISC_MODESWITCHLATE)) {
1962 var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
1963 fb_set_var(info, &var);
1967 fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
1969 if (!info->fbops->fb_blank) {
1971 unsigned short oldc;
1975 oldc = vc->vc_video_erase_char;
1976 vc->vc_video_erase_char &= charmask;
1977 height = vc->vc_rows;
1978 y_break = p->vrows - p->yscroll;
1979 if (height > y_break) {
1980 accel_clear(vc, info, real_y(p, 0),
1981 0, y_break, vc->vc_cols);
1982 accel_clear(vc, info, real_y(p, y_break),
1983 0, height - y_break,
1986 accel_clear(vc, info, real_y(p, 0),
1987 0, height, vc->vc_cols);
1988 vc->vc_video_erase_char = oldc;
1990 update_screen(vc->vc_num);
1993 return fb_blank(info, blank);
1996 static void fbcon_free_font(struct display *p)
1998 if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
1999 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
2004 static inline int fbcon_get_font(struct vc_data *vc, struct console_font_op *op)
2006 u8 *fontdata = vc->vc_font.data;
2007 u8 *data = op->data;
2010 op->width = vc->vc_font.width;
2011 op->height = vc->vc_font.height;
2012 op->charcount = vc->vc_hi_font_mask ? 512 : 256;
2016 if (op->width <= 8) {
2017 j = vc->vc_font.height;
2018 for (i = 0; i < op->charcount; i++) {
2019 memcpy(data, fontdata, j);
2020 memset(data + j, 0, 32 - j);
2024 } else if (op->width <= 16) {
2025 j = vc->vc_font.height * 2;
2026 for (i = 0; i < op->charcount; i++) {
2027 memcpy(data, fontdata, j);
2028 memset(data + j, 0, 64 - j);
2032 } else if (op->width <= 24) {
2033 for (i = 0; i < op->charcount; i++) {
2034 for (j = 0; j < vc->vc_font.height; j++) {
2035 *data++ = fontdata[0];
2036 *data++ = fontdata[1];
2037 *data++ = fontdata[2];
2038 fontdata += sizeof(u32);
2040 memset(data, 0, 3 * (32 - j));
2041 data += 3 * (32 - j);
2044 j = vc->vc_font.height * 4;
2045 for (i = 0; i < op->charcount; i++) {
2046 memcpy(data, fontdata, j);
2047 memset(data + j, 0, 128 - j);
2055 static int fbcon_do_set_font(struct vc_data *vc, struct console_font_op *op,
2056 u8 * data, int userfont)
2058 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2059 struct display *p = &fb_display[vc->vc_num];
2064 char *old_data = NULL;
2067 if (userfont && op->op != KD_FONT_OP_COPY)
2068 kfree(data - FONT_EXTRA_WORDS * sizeof(int));
2072 if (CON_IS_VISIBLE(vc) && softback_lines)
2073 fbcon_set_origin(vc);
2075 resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
2077 old_data = vc->vc_font.data;
2079 cnt = FNTCHARCNT(data);
2082 vc->vc_font.data = p->fontdata = data;
2083 if ((p->userfont = userfont))
2085 vc->vc_font.width = w;
2086 vc->vc_font.height = h;
2087 if (vc->vc_hi_font_mask && cnt == 256) {
2088 vc->vc_hi_font_mask = 0;
2089 if (vc->vc_can_do_color) {
2090 vc->vc_complement_mask >>= 1;
2091 vc->vc_s_complement_mask >>= 1;
2094 /* ++Edmund: reorder the attribute bits */
2095 if (vc->vc_can_do_color) {
2096 unsigned short *cp =
2097 (unsigned short *) vc->vc_origin;
2098 int count = vc->vc_screenbuf_size / 2;
2100 for (; count > 0; count--, cp++) {
2102 scr_writew(((c & 0xfe00) >> 1) |
2105 c = vc->vc_video_erase_char;
2106 vc->vc_video_erase_char =
2107 ((c & 0xfe00) >> 1) | (c & 0xff);
2110 } else if (!vc->vc_hi_font_mask && cnt == 512) {
2111 vc->vc_hi_font_mask = 0x100;
2112 if (vc->vc_can_do_color) {
2113 vc->vc_complement_mask <<= 1;
2114 vc->vc_s_complement_mask <<= 1;
2117 /* ++Edmund: reorder the attribute bits */
2119 unsigned short *cp =
2120 (unsigned short *) vc->vc_origin;
2121 int count = vc->vc_screenbuf_size / 2;
2123 for (; count > 0; count--, cp++) {
2124 unsigned short newc;
2126 if (vc->vc_can_do_color)
2128 ((c & 0xff00) << 1) | (c &
2132 scr_writew(newc, cp);
2134 c = vc->vc_video_erase_char;
2135 if (vc->vc_can_do_color) {
2136 vc->vc_video_erase_char =
2137 ((c & 0xff00) << 1) | (c & 0xff);
2140 vc->vc_video_erase_char = c & ~0x100;
2146 /* reset wrap/pan */
2147 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
2148 vc_resize(vc->vc_num, info->var.xres / w, info->var.yres / h);
2149 if (CON_IS_VISIBLE(vc) && softback_buf) {
2150 int l = fbcon_softback_size / vc->vc_size_row;
2153 softback_buf + l * vc->vc_size_row;
2155 /* Smaller scrollback makes no sense, and 0 would screw
2156 the operation totally */
2160 } else if (CON_IS_VISIBLE(vc)
2161 && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
2162 accel_clear_margins(vc, info, 0);
2163 update_screen(vc->vc_num);
2166 if (old_data && (--REFCOUNT(old_data) == 0))
2167 kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
2171 static inline int fbcon_copy_font(struct vc_data *vc, struct console_font_op *op)
2176 if (h < 0 || !vc_cons_allocated(h))
2178 if (h == vc->vc_num)
2179 return 0; /* nothing to do */
2180 od = &fb_display[h];
2181 if (od->fontdata == vc->vc_font.data)
2182 return 0; /* already the same font... */
2183 op->width = vc->vc_font.width;
2184 op->height = vc->vc_font.height;
2185 return fbcon_do_set_font(vc, op, od->fontdata, od->userfont);
2188 static inline int fbcon_set_font(struct vc_data *vc, struct console_font_op *op)
2194 u8 *new_data, *data = op->data, *p;
2196 if ((w <= 0) || (w > 32)
2197 || (op->charcount != 256 && op->charcount != 512))
2206 size *= op->charcount;
2210 kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER)))
2212 new_data += FONT_EXTRA_WORDS * sizeof(int);
2213 FNTSIZE(new_data) = size;
2214 FNTCHARCNT(new_data) = op->charcount;
2215 REFCOUNT(new_data) = 0; /* usage counter */
2218 for (i = 0; i < op->charcount; i++) {
2223 } else if (w <= 16) {
2225 for (i = 0; i < op->charcount; i++) {
2230 } else if (w <= 24) {
2231 for (i = 0; i < op->charcount; i++) {
2233 for (j = 0; j < h; j++) {
2239 data += 3 * (32 - h);
2243 for (i = 0; i < op->charcount; i++) {
2249 /* we can do it in u32 chunks because of charcount is 256 or 512, so
2250 font length must be multiple of 256, at least. And 256 is multiple
2253 while (p > new_data) {
2254 p = (u8 *)((u32 *)p - 1);
2257 FNTSUM(new_data) = k;
2258 /* Check if the same font is on some other console already */
2259 for (i = 0; i < MAX_NR_CONSOLES; i++) {
2260 struct vc_data *tmp = vc_cons[i].d;
2262 if (fb_display[i].userfont &&
2263 fb_display[i].fontdata &&
2264 FNTSUM(fb_display[i].fontdata) == k &&
2265 FNTSIZE(fb_display[i].fontdata) == size &&
2266 tmp->vc_font.width == w &&
2267 !memcmp(fb_display[i].fontdata, new_data, size)) {
2268 kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
2269 new_data = fb_display[i].fontdata;
2273 return fbcon_do_set_font(vc, op, new_data, 1);
2276 static inline int fbcon_set_def_font(struct vc_data *vc, struct console_font_op *op)
2278 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2279 char name[MAX_FONT_NAME];
2280 struct font_desc *f;
2283 f = get_default_font(info->var.xres, info->var.yres);
2284 else if (strncpy_from_user(name, op->data, MAX_FONT_NAME - 1) < 0)
2287 name[MAX_FONT_NAME - 1] = 0;
2288 if (!(f = find_font(name)))
2291 op->width = f->width;
2292 op->height = f->height;
2293 return fbcon_do_set_font(vc, op, f->data, 0);
2296 static int fbcon_font_op(struct vc_data *vc, struct console_font_op *op)
2299 case KD_FONT_OP_SET:
2300 return fbcon_set_font(vc, op);
2301 case KD_FONT_OP_GET:
2302 return fbcon_get_font(vc, op);
2303 case KD_FONT_OP_SET_DEFAULT:
2304 return fbcon_set_def_font(vc, op);
2305 case KD_FONT_OP_COPY:
2306 return fbcon_copy_font(vc, op);
2312 static u16 palette_red[16];
2313 static u16 palette_green[16];
2314 static u16 palette_blue[16];
2316 static struct fb_cmap palette_cmap = {
2317 0, 16, palette_red, palette_green, palette_blue, NULL
2320 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table)
2322 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2326 if (!vc->vc_can_do_color
2327 || (!info->fbops->fb_blank && console_blanked))
2329 for (i = j = 0; i < 16; i++) {
2331 val = vc->vc_palette[j++];
2332 palette_red[k] = (val << 8) | val;
2333 val = vc->vc_palette[j++];
2334 palette_green[k] = (val << 8) | val;
2335 val = vc->vc_palette[j++];
2336 palette_blue[k] = (val << 8) | val;
2338 if (info->var.bits_per_pixel <= 4)
2339 palette_cmap.len = 1 << info->var.bits_per_pixel;
2341 palette_cmap.len = 16;
2342 palette_cmap.start = 0;
2343 return fb_set_cmap(&palette_cmap, 1, info);
2346 static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
2351 if (vc->vc_num != fg_console || !softback_lines)
2352 return (u16 *) (vc->vc_origin + offset);
2353 line = offset / vc->vc_size_row;
2354 if (line >= softback_lines)
2355 return (u16 *) (vc->vc_origin + offset -
2356 softback_lines * vc->vc_size_row);
2357 p = softback_curr + offset;
2358 if (p >= softback_end)
2359 p += softback_buf - softback_end;
2363 static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
2369 if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
2370 unsigned long offset = (pos - vc->vc_origin) / 2;
2372 x = offset % vc->vc_cols;
2373 y = offset / vc->vc_cols;
2374 if (vc->vc_num == fg_console)
2375 y += softback_lines;
2376 ret = pos + (vc->vc_cols - x) * 2;
2377 } else if (vc->vc_num == fg_console && softback_lines) {
2378 unsigned long offset = pos - softback_curr;
2380 if (pos < softback_curr)
2381 offset += softback_end - softback_buf;
2383 x = offset % vc->vc_cols;
2384 y = offset / vc->vc_cols;
2385 ret = pos + (vc->vc_cols - x) * 2;
2386 if (ret == softback_end)
2388 if (ret == softback_in)
2389 ret = vc->vc_origin;
2391 /* Should not happen */
2393 ret = vc->vc_origin;
2402 /* As we might be inside of softback, we may work with non-contiguous buffer,
2403 that's why we have to use a separate routine. */
2404 static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
2407 u16 a = scr_readw(p);
2408 if (!vc->vc_can_do_color)
2410 else if (vc->vc_hi_font_mask == 0x100)
2411 a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
2412 (((a) & 0x0e00) << 4);
2414 a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
2415 (((a) & 0x0700) << 4);
2417 if (p == (u16 *) softback_end)
2418 p = (u16 *) softback_buf;
2419 if (p == (u16 *) softback_in)
2420 p = (u16 *) vc->vc_origin;
2424 static int fbcon_scrolldelta(struct vc_data *vc, int lines)
2426 struct fb_info *info = registered_fb[(int) con2fb_map[fg_console]];
2427 struct display *p = &fb_display[fg_console];
2428 int offset, limit, scrollback_old;
2431 if (vc->vc_num != fg_console)
2433 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT || !lines)
2435 if (logo_shown >= 0) {
2436 struct vc_data *conp2 = vc_cons[logo_shown].d;
2438 if (conp2->vc_top == logo_lines
2439 && conp2->vc_bottom == conp2->vc_rows)
2441 if (logo_shown == vc->vc_num) {
2447 logo_lines * vc->vc_size_row;
2448 for (i = 0; i < logo_lines; i++) {
2449 if (p == softback_top)
2451 if (p == softback_buf)
2453 p -= vc->vc_size_row;
2454 q -= vc->vc_size_row;
2455 scr_memcpyw((u16 *) q, (u16 *) p,
2459 update_region(vc->vc_num, vc->vc_origin,
2460 logo_lines * vc->vc_cols);
2464 fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK);
2465 fbcon_redraw_softback(vc, p, lines);
2466 fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK);
2470 if (!scrollback_phys_max)
2473 scrollback_old = scrollback_current;
2474 scrollback_current -= lines;
2475 if (scrollback_current < 0)
2476 scrollback_current = 0;
2477 else if (scrollback_current > scrollback_max)
2478 scrollback_current = scrollback_max;
2479 if (scrollback_current == scrollback_old)
2482 if (!info->fbops->fb_blank &&
2483 (console_blanked || vt_cons[vc->vc_num]->vc_mode != KD_TEXT
2486 fbcon_cursor(vc, CM_ERASE);
2488 offset = p->yscroll - scrollback_current;
2490 switch (p->scrollmode) {
2491 case SCROLL_WRAP_MOVE:
2492 info->var.vmode |= FB_VMODE_YWRAP;
2494 case SCROLL_PAN_MOVE:
2495 case SCROLL_PAN_REDRAW:
2496 limit -= vc->vc_rows;
2497 info->var.vmode &= ~FB_VMODE_YWRAP;
2502 else if (offset >= limit)
2504 info->var.xoffset = 0;
2505 info->var.yoffset = offset * vc->vc_font.height;
2506 update_var(vc->vc_num, info);
2507 if (!scrollback_current)
2508 fbcon_cursor(vc, CM_DRAW);
2512 static int fbcon_set_origin(struct vc_data *vc)
2514 if (softback_lines && !console_blanked)
2515 fbcon_scrolldelta(vc, softback_lines);
2519 static void fbcon_suspended(struct fb_info *info)
2521 /* Clear cursor, restore saved data */
2522 info->cursor.enable = 0;
2523 info->fbops->fb_cursor(info, &info->cursor);
2526 static void fbcon_resumed(struct fb_info *info)
2530 if (info->currcon < 0)
2532 vc = vc_cons[info->currcon].d;
2534 update_screen(vc->vc_num);
2537 static void fbcon_modechanged(struct fb_info *info)
2539 struct vc_data *vc = vc_cons[info->currcon].d;
2543 if (info->currcon < 0 || vt_cons[info->currcon]->vc_mode !=
2546 p = &fb_display[vc->vc_num];
2548 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
2549 vc->vc_can_do_color = info->var.bits_per_pixel != 1;
2550 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
2552 if (CON_IS_VISIBLE(vc)) {
2553 cols = info->var.xres / vc->vc_font.width;
2554 rows = info->var.yres / vc->vc_font.height;
2555 vc_resize(vc->vc_num, cols, rows);
2556 updatescrollmode(p, info, vc);
2558 scrollback_current = 0;
2559 update_var(vc->vc_num, info);
2560 fbcon_set_palette(vc, color_table);
2561 update_screen(vc->vc_num);
2563 int l = fbcon_softback_size / vc->vc_size_row;
2565 softback_end = softback_buf + l * vc->vc_size_row;
2567 /* Smaller scrollback makes no sense, and 0
2568 would screw the operation totally */
2575 static int fbcon_event_notify(struct notifier_block *self,
2576 unsigned long action, void *data)
2578 struct fb_info *info = (struct fb_info *) data;
2581 case FB_EVENT_SUSPEND:
2582 fbcon_suspended(info);
2584 case FB_EVENT_RESUME:
2585 fbcon_resumed(info);
2587 case FB_EVENT_MODE_CHANGE:
2588 fbcon_modechanged(info);
2596 * The console `switch' structure for the frame buffer based console
2599 const struct consw fb_con = {
2600 .owner = THIS_MODULE,
2601 .con_startup = fbcon_startup,
2602 .con_init = fbcon_init,
2603 .con_deinit = fbcon_deinit,
2604 .con_clear = fbcon_clear,
2605 .con_putc = fbcon_putc,
2606 .con_putcs = fbcon_putcs,
2607 .con_cursor = fbcon_cursor,
2608 .con_scroll = fbcon_scroll,
2609 .con_bmove = fbcon_bmove,
2610 .con_switch = fbcon_switch,
2611 .con_blank = fbcon_blank,
2612 .con_font_op = fbcon_font_op,
2613 .con_set_palette = fbcon_set_palette,
2614 .con_scrolldelta = fbcon_scrolldelta,
2615 .con_set_origin = fbcon_set_origin,
2616 .con_invert_region = fbcon_invert_region,
2617 .con_screen_pos = fbcon_screen_pos,
2618 .con_getxy = fbcon_getxy,
2619 .con_resize = fbcon_resize,
2622 static struct notifier_block fbcon_event_notifier = {
2623 .notifier_call = fbcon_event_notify,
2625 static int fbcon_event_notifier_registered;
2627 /* can't be __init as it can be called by set_con2fb_map() later */
2628 int fb_console_init(void)
2632 for (i = 0; i < MAX_NR_CONSOLES; i++)
2635 if (!num_registered_fb)
2638 if (info_idx == -1) {
2639 for (i = 0; i < FB_MAX; i++) {
2640 if (registered_fb[i] != NULL) {
2646 for (i = first_fb_vc; i <= last_fb_vc; i++)
2647 con2fb_map[i] = info_idx;
2648 err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
2651 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2656 acquire_console_sem();
2657 if (!fbcon_event_notifier_registered) {
2658 fb_register_client(&fbcon_event_notifier);
2659 fbcon_event_notifier_registered = 1;
2661 release_console_sem();
2667 void __exit fb_console_exit(void)
2669 acquire_console_sem();
2670 if (fbcon_event_notifier_registered) {
2671 fb_unregister_client(&fbcon_event_notifier);
2672 fbcon_event_notifier_registered = 0;
2674 release_console_sem();
2675 give_up_console(&fb_con);
2678 module_init(fb_console_init);
2679 module_exit(fb_console_exit);
2684 * Visible symbols for modules
2687 EXPORT_SYMBOL(fb_display);
2688 EXPORT_SYMBOL(fb_con);
2690 MODULE_LICENSE("GPL");