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_set_palette(struct vc_data *vc, unsigned char *table);
169 static int fbcon_scrolldelta(struct vc_data *vc, int lines);
170 void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
177 static __inline__ int real_y(struct display *p, int ypos);
178 static __inline__ void ywrap_up(struct vc_data *vc, int count);
179 static __inline__ void ywrap_down(struct vc_data *vc, int count);
180 static __inline__ void ypan_up(struct vc_data *vc, int count);
181 static __inline__ void ypan_down(struct vc_data *vc, int count);
182 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
183 int dy, int dx, int height, int width, u_int y_break);
184 static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc);
185 static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
186 int line, int count, int dy);
190 * On the Macintoy, there may or may not be a working VBL int. We need to probe
192 static int vbl_detected;
194 static irqreturn_t fb_vbl_detect(int irq, void *dummy, struct pt_regs *fp)
201 static void fb_flashcursor(void *private)
203 struct fb_info *info = (struct fb_info *) private;
204 struct vc_data *vc = NULL;
206 if (info->currcon != -1)
207 vc = vc_cons[info->currcon].d;
209 if (info->state != FBINFO_STATE_RUNNING ||
210 info->cursor.rop == ROP_COPY || !vc || !CON_IS_VISIBLE(vc)
211 || registered_fb[(int) con2fb_map[vc->vc_num]] != info)
213 acquire_console_sem();
214 info->cursor.enable ^= 1;
215 info->fbops->fb_cursor(info, &info->cursor);
216 release_console_sem();
219 #if (defined(__arm__) && defined(IRQ_VSYNCPULSE)) || defined(CONFIG_ATARI) || defined(CONFIG_MAC)
220 static int cursor_blink_rate;
221 static irqreturn_t fb_vbl_handler(int irq, void *dev_id, struct pt_regs *fp)
223 struct fb_info *info = dev_id;
225 if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
226 schedule_work(&info->queue);
227 vbl_cursor_cnt = cursor_blink_rate;
233 static void cursor_timer_handler(unsigned long dev_addr)
235 struct fb_info *info = (struct fb_info *) dev_addr;
237 schedule_work(&info->queue);
238 mod_timer(&info->cursor_timer, jiffies + HZ/5);
241 int __init fb_console_setup(char *this_opt)
246 if (!this_opt || !*this_opt)
249 while ((options = strsep(&this_opt, ",")) != NULL) {
250 if (!strncmp(options, "font:", 5))
251 strcpy(fontname, options + 5);
253 if (!strncmp(options, "scrollback:", 11)) {
256 fbcon_softback_size = simple_strtoul(options, &options, 0);
257 if (*options == 'k' || *options == 'K') {
258 fbcon_softback_size *= 1024;
268 if (!strncmp(options, "map:", 4)) {
271 for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
274 con2fb_map[i] = (options[j++]-'0') % FB_MAX;
279 if (!strncmp(options, "vc:", 3)) {
282 first_fb_vc = simple_strtoul(options, &options, 10) - 1;
285 if (*options++ == '-')
286 last_fb_vc = simple_strtoul(options, &options, 10) - 1;
287 fbcon_is_default = 0;
293 __setup("fbcon=", fb_console_setup);
295 static int search_fb_in_map(int idx)
299 for (i = 0; i < MAX_NR_CONSOLES; i++) {
300 if (con2fb_map[i] == idx)
306 static int search_for_mapped_con(void)
310 for (i = 0; i < MAX_NR_CONSOLES; i++) {
311 if (con2fb_map[i] != -1)
318 * set_con2fb_map - map console to frame buffer device
319 * @unit: virtual console number to map
320 * @newidx: frame buffer index to map virtual console to
322 * Maps a virtual console @unit to a frame buffer device
325 int set_con2fb_map(int unit, int newidx)
327 struct vc_data *vc = vc_cons[unit].d;
328 int oldidx = con2fb_map[unit];
329 struct fb_info *info = registered_fb[newidx];
330 struct fb_info *oldinfo = NULL;
333 if (oldidx == newidx)
339 if (!search_for_mapped_con()) {
346 oldinfo = registered_fb[oldidx];
348 found = search_fb_in_map(newidx);
350 acquire_console_sem();
351 con2fb_map[unit] = newidx;
353 if (!try_module_get(info->fbops->owner)) {
354 con2fb_map[unit] = oldidx;
355 release_console_sem();
358 if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
359 module_put(info->fbops->owner);
360 con2fb_map[unit] = oldidx;
361 release_console_sem();
367 * If old fb is not mapped to any of the consoles,
368 * fbcon should release it.
370 if (oldinfo && !search_fb_in_map(oldidx)) {
371 if (oldinfo->fbops->fb_release &&
372 oldinfo->fbops->fb_release(oldinfo, 0)) {
373 con2fb_map[unit] = oldidx;
374 if (!found && info->fbops->fb_release)
375 info->fbops->fb_release(info, 0);
377 module_put(info->fbops->owner);
378 release_console_sem();
381 if (oldinfo->queue.func == fb_flashcursor)
382 del_timer_sync(&oldinfo->cursor_timer);
383 module_put(oldinfo->fbops->owner);
387 if (!info->queue.func || info->queue.func == fb_flashcursor) {
388 if (!info->queue.func)
389 INIT_WORK(&info->queue, fb_flashcursor, info);
391 init_timer(&info->cursor_timer);
392 info->cursor_timer.function = cursor_timer_handler;
393 info->cursor_timer.expires = jiffies + HZ / 5;
394 info->cursor_timer.data = (unsigned long ) info;
395 add_timer(&info->cursor_timer);
398 if (info->fbops->fb_set_par)
399 info->fbops->fb_set_par(info);
400 fbcon_set_disp(info, vc);
401 release_console_sem();
406 * Accelerated handlers.
408 void accel_bmove(struct vc_data *vc, struct fb_info *info, int sy,
409 int sx, int dy, int dx, int height, int width)
411 struct fb_copyarea area;
413 area.sx = sx * vc->vc_font.width;
414 area.sy = sy * vc->vc_font.height;
415 area.dx = dx * vc->vc_font.width;
416 area.dy = dy * vc->vc_font.height;
417 area.height = height * vc->vc_font.height;
418 area.width = width * vc->vc_font.width;
420 info->fbops->fb_copyarea(info, &area);
423 void accel_clear(struct vc_data *vc, struct fb_info *info, int sy,
424 int sx, int height, int width)
426 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
427 struct fb_fillrect region;
429 region.color = attr_bgcol_ec(bgshift, vc);
430 region.dx = sx * vc->vc_font.width;
431 region.dy = sy * vc->vc_font.height;
432 region.width = width * vc->vc_font.width;
433 region.height = height * vc->vc_font.height;
434 region.rop = ROP_COPY;
436 info->fbops->fb_fillrect(info, ®ion);
439 void accel_putcs(struct vc_data *vc, struct fb_info *info,
440 const unsigned short *s, int count, int yy, int xx)
442 void (*move_unaligned)(struct fb_info *info, struct fb_pixmap *buf,
443 u8 *dst, u32 d_pitch, u8 *src, u32 idx,
444 u32 height, u32 shift_high, u32 shift_low,
446 void (*move_aligned)(struct fb_info *info, struct fb_pixmap *buf,
447 u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch,
449 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
450 unsigned int width = (vc->vc_font.width + 7) >> 3;
451 unsigned int cellsize = vc->vc_font.height * width;
452 unsigned int maxcnt = info->pixmap.size/cellsize;
453 unsigned int scan_align = info->pixmap.scan_align - 1;
454 unsigned int buf_align = info->pixmap.buf_align - 1;
455 unsigned int shift_low = 0, mod = vc->vc_font.width % 8;
456 unsigned int shift_high = 8, pitch, cnt, size, k;
457 unsigned int idx = vc->vc_font.width >> 3;
458 struct fb_image image;
461 image.fg_color = attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8,
463 image.bg_color = attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12,
465 image.dx = xx * vc->vc_font.width;
466 image.dy = yy * vc->vc_font.height;
467 image.height = vc->vc_font.height;
470 if (info->pixmap.outbuf && info->pixmap.inbuf) {
471 move_aligned = fb_iomove_buf_aligned;
472 move_unaligned = fb_iomove_buf_unaligned;
474 move_aligned = fb_sysmove_buf_aligned;
475 move_unaligned = fb_sysmove_buf_unaligned;
483 image.width = vc->vc_font.width * cnt;
484 pitch = ((image.width + 7) >> 3) + scan_align;
485 pitch &= ~scan_align;
486 size = pitch * image.height + buf_align;
488 dst = fb_get_buffer_offset(info, &info->pixmap, size);
492 src = vc->vc_font.data + (scr_readw(s++)&
494 move_unaligned(info, &info->pixmap, dst, pitch,
495 src, idx, image.height,
496 shift_high, shift_low, mod);
498 dst += (shift_low >= 8) ? width : width - 1;
500 shift_high = 8 - shift_low;
504 src = vc->vc_font.data + (scr_readw(s++)&
506 move_aligned(info, &info->pixmap, dst, pitch,
507 src, idx, image.height);
511 info->fbops->fb_imageblit(info, &image);
512 image.dx += cnt * vc->vc_font.width;
517 void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
520 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
521 unsigned int cw = vc->vc_font.width;
522 unsigned int ch = vc->vc_font.height;
523 unsigned int rw = info->var.xres - (vc->vc_cols*cw);
524 unsigned int bh = info->var.yres - (vc->vc_rows*ch);
525 unsigned int rs = info->var.xres - rw;
526 unsigned int bs = info->var.yres - bh;
527 struct fb_fillrect region;
529 region.color = attr_bgcol_ec(bgshift, vc);
530 region.rop = ROP_COPY;
532 if (rw && !bottom_only) {
533 region.dx = info->var.xoffset + rs;
536 region.height = info->var.yres_virtual;
537 info->fbops->fb_fillrect(info, ®ion);
541 region.dx = info->var.xoffset;
542 region.dy = info->var.yoffset + bs;
545 info->fbops->fb_fillrect(info, ®ion);
550 * Low Level Operations
552 /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */
553 static const char *fbcon_startup(void)
555 const char *display_desc = "frame buffer device";
556 struct display *p = &fb_display[fg_console];
557 struct vc_data *vc = vc_cons[fg_console].d;
558 struct font_desc *font = NULL;
559 struct module *owner;
560 struct fb_info *info = NULL;
566 * If num_registered_fb is zero, this is a call for the dummy part.
567 * The frame buffer devices weren't initialized yet.
569 if (!num_registered_fb || info_idx == -1)
572 * Instead of blindly using registered_fb[0], we use info_idx, set by
575 info = registered_fb[info_idx];
580 owner = info->fbops->owner;
581 if (!try_module_get(owner))
583 if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
587 if (info->fix.type != FB_TYPE_TEXT) {
588 if (fbcon_softback_size) {
592 kmalloc(fbcon_softback_size,
595 fbcon_softback_size = 0;
601 kfree((void *) softback_buf);
607 softback_in = softback_top = softback_curr =
612 /* Setup default font */
614 if (!fontname[0] || !(font = find_font(fontname)))
615 font = get_default_font(info->var.xres,
617 vc->vc_font.width = font->width;
618 vc->vc_font.height = font->height;
619 vc->vc_font.data = p->fontdata = font->data;
620 vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */
623 cols = info->var.xres / vc->vc_font.width;
624 rows = info->var.yres / vc->vc_font.height;
625 vc_resize(vc->vc_num, cols, rows);
627 DPRINTK("mode: %s\n", info->fix.id);
628 DPRINTK("visual: %d\n", info->fix.visual);
629 DPRINTK("res: %dx%d-%d\n", info->var.xres,
631 info->var.bits_per_pixel);
632 con_set_default_unimap(vc->vc_num);
636 cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
638 request_irq(IRQ_AUTO_4, fb_vbl_handler,
639 IRQ_TYPE_PRIO, "framebuffer vbl",
642 #endif /* CONFIG_ATARI */
646 * On a Macintoy, the VBL interrupt may or may not be active.
647 * As interrupt based cursor is more reliable and race free, we
648 * probe for VBL interrupts.
653 * Probe for VBL: set temp. handler ...
655 irqres = request_irq(IRQ_MAC_VBL, fb_vbl_detect, 0,
656 "framebuffer vbl", info);
660 * ... and spin for 20 ms ...
662 while (!vbl_detected && ++ct < 1000)
667 ("fbcon_startup: No VBL detected, using timer based cursor.\n");
669 free_irq(IRQ_MAC_VBL, fb_vbl_detect);
673 * interrupt based cursor ok
675 cursor_blink_rate = MAC_CURSOR_BLINK_RATE;
677 request_irq(IRQ_MAC_VBL, fb_vbl_handler, 0,
678 "framebuffer vbl", info);
681 * VBL not detected: fall through, use timer based cursor
686 #endif /* CONFIG_MAC */
688 #if defined(__arm__) && defined(IRQ_VSYNCPULSE)
689 cursor_blink_rate = ARM_CURSOR_BLINK_RATE;
690 irqres = request_irq(IRQ_VSYNCPULSE, fb_vbl_handler, SA_SHIRQ,
691 "framebuffer vbl", info);
693 /* Initialize the work queue. If the driver provides its
694 * own work queue this means it will use something besides
695 * default timer to flash the cursor. */
696 if (!info->queue.func) {
697 INIT_WORK(&info->queue, fb_flashcursor, info);
699 init_timer(&info->cursor_timer);
700 info->cursor_timer.function = cursor_timer_handler;
701 info->cursor_timer.expires = jiffies + HZ / 5;
702 info->cursor_timer.data = (unsigned long ) info;
703 add_timer(&info->cursor_timer);
708 static void fbcon_init(struct vc_data *vc, int init)
710 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
711 struct vc_data **default_mode = vc->vc_display_fg;
712 struct display *t, *p = &fb_display[vc->vc_num];
713 int display_fg = (*default_mode)->vc_num;
714 int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;
715 unsigned short *save = NULL, *r, *q;
716 int cap = info->flags;
718 if (info_idx == -1 || info == NULL)
720 if (vc->vc_num != display_fg || (info->flags & FBINFO_MODULE) ||
721 (info->fix.type == FB_TYPE_TEXT))
724 info->var.xoffset = info->var.yoffset = p->yscroll = 0; /* reset wrap/pan */
726 /* If we are not the first console on this
727 fb, copy the font from that console */
728 t = &fb_display[display_fg];
729 if (!vc->vc_font.data) {
730 vc->vc_font.data = p->fontdata = t->fontdata;
731 vc->vc_font.width = (*default_mode)->vc_font.width;
732 vc->vc_font.height = (*default_mode)->vc_font.height;
733 p->userfont = t->userfont;
735 REFCOUNT(p->fontdata)++;
736 con_copy_unimap(vc->vc_num, display_fg);
739 charcnt = FNTCHARCNT(p->fontdata);
740 vc->vc_can_do_color = info->var.bits_per_pixel != 1;
741 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
742 if (charcnt == 256) {
743 vc->vc_hi_font_mask = 0;
745 vc->vc_hi_font_mask = 0x100;
746 if (vc->vc_can_do_color)
747 vc->vc_complement_mask <<= 1;
751 new_cols = info->var.xres / vc->vc_font.width;
752 new_rows = info->var.yres / vc->vc_font.height;
753 vc_resize(vc->vc_num, new_cols, new_rows);
755 * We must always set the mode. The mode of the previous console
756 * driver could be in the same resolution but we are using different
757 * hardware so we have to initialize the hardware.
759 * We need to do it in fbcon_init() to prevent screen corruption.
761 if (CON_IS_VISIBLE(vc) && info->fbops->fb_set_par)
762 info->fbops->fb_set_par(info);
765 if ((cap & FBINFO_HWACCEL_COPYAREA) &&
766 !(cap & FBINFO_HWACCEL_DISABLED))
767 p->scrollmode = SCROLL_MOVE;
768 else /* default to something safe */
769 p->scrollmode = SCROLL_REDRAW;
772 * ++guenther: console.c:vc_allocate() relies on initializing
773 * vc_{cols,rows}, but we must not set those if we are only
774 * resizing the console.
777 vc->vc_cols = new_cols;
778 vc->vc_rows = new_rows;
782 /* Need to make room for the logo */
786 logo_height = fb_prepare_logo(info);
787 logo_lines = (logo_height + vc->vc_font.height - 1) /
789 q = (unsigned short *) (vc->vc_origin +
790 vc->vc_size_row * rows);
791 step = logo_lines * cols;
792 for (r = q - logo_lines * cols; r < q; r++)
793 if (scr_readw(r) != vc->vc_video_erase_char)
795 if (r != q && new_rows >= rows + logo_lines) {
796 save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL);
798 int i = cols < new_cols ? cols : new_cols;
799 scr_memsetw(save, vc->vc_video_erase_char,
800 logo_lines * new_cols * 2);
802 for (cnt = 0; cnt < logo_lines; cnt++, r += i)
803 scr_memcpyw(save + cnt * new_cols, r, 2 * i);
808 /* We can scroll screen down */
810 for (cnt = rows - logo_lines; cnt > 0; cnt--) {
811 scr_memcpyw(r + step, r, vc->vc_size_row);
815 vc->vc_y += logo_lines;
816 vc->vc_pos += logo_lines * vc->vc_size_row;
819 scr_memsetw((unsigned short *) vc->vc_origin,
820 vc->vc_video_erase_char,
821 vc->vc_size_row * logo_lines);
823 if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
824 accel_clear_margins(vc, info, 0);
825 update_screen(vc->vc_num);
828 q = (unsigned short *) (vc->vc_origin +
831 scr_memcpyw(q, save, logo_lines * new_cols * 2);
832 vc->vc_y += logo_lines;
833 vc->vc_pos += logo_lines * vc->vc_size_row;
836 if (logo_lines > vc->vc_bottom) {
839 "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
842 vc->vc_top = logo_lines;
846 if (vc->vc_num == display_fg && softback_buf) {
847 int l = fbcon_softback_size / vc->vc_size_row;
849 softback_end = softback_buf + l * vc->vc_size_row;
851 /* Smaller scrollback makes no sense, and 0 would screw
852 the operation totally */
858 static void fbcon_deinit(struct vc_data *vc)
860 struct display *p = &fb_display[vc->vc_num];
867 /* ====================================================================== */
869 /* fbcon_XXX routines - interface used by the world
871 * This system is now divided into two levels because of complications
872 * caused by hardware scrolling. Top level functions:
874 * fbcon_bmove(), fbcon_clear(), fbcon_putc()
876 * handles y values in range [0, scr_height-1] that correspond to real
877 * screen positions. y_wrap shift means that first line of bitmap may be
878 * anywhere on this display. These functions convert lineoffsets to
879 * bitmap offsets and deal with the wrap-around case by splitting blits.
881 * fbcon_bmove_physical_8() -- These functions fast implementations
882 * fbcon_clear_physical_8() -- of original fbcon_XXX fns.
883 * fbcon_putc_physical_8() -- (font width != 8) may be added later
887 * At the moment fbcon_putc() cannot blit across vertical wrap boundary
888 * Implies should only really hardware scroll in rows. Only reason for
889 * restriction is simplicity & efficiency at the moment.
892 static __inline__ int real_y(struct display *p, int ypos)
897 return ypos < rows ? ypos : ypos - rows;
901 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
904 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
906 struct display *p = &fb_display[vc->vc_num];
909 if (!info->fbops->fb_blank && console_blanked)
911 if (info->state != FBINFO_STATE_RUNNING)
914 if (!height || !width)
917 /* Split blits that cross physical y_wrap boundary */
919 y_break = p->vrows - p->yscroll;
920 if (sy < y_break && sy + height - 1 >= y_break) {
921 u_int b = y_break - sy;
922 accel_clear(vc, info, real_y(p, sy), sx, b, width);
923 accel_clear(vc, info, real_y(p, sy + b), sx, height - b,
926 accel_clear(vc, info, real_y(p, sy), sx, height, width);
929 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
931 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
932 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
933 unsigned int scan_align = info->pixmap.scan_align - 1;
934 unsigned int buf_align = info->pixmap.buf_align - 1;
935 unsigned int width = (vc->vc_font.width + 7) >> 3;
936 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
937 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
938 struct display *p = &fb_display[vc->vc_num];
939 unsigned int size, pitch;
940 struct fb_image image;
943 if (!info->fbops->fb_blank && console_blanked)
945 if (info->state != FBINFO_STATE_RUNNING)
948 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
951 image.dx = xpos * vc->vc_font.width;
952 image.dy = real_y(p, ypos) * vc->vc_font.height;
953 image.width = vc->vc_font.width;
954 image.height = vc->vc_font.height;
955 image.fg_color = attr_fgcol(fgshift, c);
956 image.bg_color = attr_bgcol(bgshift, c);
959 src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * width;
961 pitch = width + scan_align;
962 pitch &= ~scan_align;
963 size = pitch * vc->vc_font.height;
967 dst = fb_get_buffer_offset(info, &info->pixmap, size);
970 if (info->pixmap.outbuf)
971 fb_iomove_buf_aligned(info, &info->pixmap, dst, pitch, src, width, image.height);
973 fb_sysmove_buf_aligned(info, &info->pixmap, dst, pitch, src, width, image.height);
975 info->fbops->fb_imageblit(info, &image);
978 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
979 int count, int ypos, int xpos)
981 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
982 struct display *p = &fb_display[vc->vc_num];
984 if (!info->fbops->fb_blank && console_blanked)
986 if (info->state != FBINFO_STATE_RUNNING)
989 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
992 accel_putcs(vc, info, s, count, real_y(p, ypos), xpos);
995 static void fbcon_cursor(struct vc_data *vc, int mode)
997 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
998 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
999 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
1000 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
1001 struct display *p = &fb_display[vc->vc_num];
1002 int w = (vc->vc_font.width + 7) >> 3, c;
1003 int y = real_y(p, vc->vc_y);
1004 struct fb_cursor cursor;
1006 if (mode & CM_SOFTBACK) {
1007 mode &= ~CM_SOFTBACK;
1008 if (softback_lines) {
1009 if (y + softback_lines >= vc->vc_rows)
1012 y += softback_lines;
1014 } else if (softback_lines)
1015 fbcon_set_origin(vc);
1017 c = scr_readw((u16 *) vc->vc_pos);
1019 cursor.image.data = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
1020 cursor.set = FB_CUR_SETCUR;
1021 cursor.image.depth = 1;
1025 if (info->cursor.rop == ROP_XOR) {
1026 info->cursor.enable = 0;
1027 info->cursor.rop = ROP_COPY;
1028 info->fbops->fb_cursor(info, &cursor);
1033 info->cursor.enable = 1;
1035 if (info->cursor.image.fg_color != attr_fgcol(fgshift, c) ||
1036 info->cursor.image.bg_color != attr_bgcol(bgshift, c)) {
1037 cursor.image.fg_color = attr_fgcol(fgshift, c);
1038 cursor.image.bg_color = attr_bgcol(bgshift, c);
1039 cursor.set |= FB_CUR_SETCMAP;
1042 if ((info->cursor.image.dx != (vc->vc_font.width * vc->vc_x)) ||
1043 (info->cursor.image.dy != (vc->vc_font.height * y))) {
1044 cursor.image.dx = vc->vc_font.width * vc->vc_x;
1045 cursor.image.dy = vc->vc_font.height * y;
1046 cursor.set |= FB_CUR_SETPOS;
1049 if (info->cursor.image.height != vc->vc_font.height ||
1050 info->cursor.image.width != vc->vc_font.width) {
1051 cursor.image.height = vc->vc_font.height;
1052 cursor.image.width = vc->vc_font.width;
1053 cursor.set |= FB_CUR_SETSIZE;
1056 if (info->cursor.hot.x || info->cursor.hot.y) {
1057 cursor.hot.x = cursor.hot.y = 0;
1058 cursor.set |= FB_CUR_SETHOT;
1061 if ((cursor.set & FB_CUR_SETSIZE) || ((vc->vc_cursor_type & 0x0f) != p->cursor_shape)
1062 || info->cursor.mask == NULL) {
1063 char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
1064 int cur_height, size, i = 0;
1068 if (info->cursor.mask)
1069 kfree(info->cursor.mask);
1070 info->cursor.mask = mask;
1072 p->cursor_shape = vc->vc_cursor_type & 0x0f;
1073 cursor.set |= FB_CUR_SETSHAPE;
1075 switch (vc->vc_cursor_type & 0x0f) {
1080 cur_height = (vc->vc_font.height < 10) ? 1 : 2;
1082 case CUR_LOWER_THIRD:
1083 cur_height = vc->vc_font.height/3;
1085 case CUR_LOWER_HALF:
1086 cur_height = vc->vc_font.height >> 1;
1088 case CUR_TWO_THIRDS:
1089 cur_height = (vc->vc_font.height << 1)/3;
1093 cur_height = vc->vc_font.height;
1096 size = (vc->vc_font.height - cur_height) * w;
1099 size = cur_height * w;
1103 info->cursor.rop = ROP_XOR;
1104 info->fbops->fb_cursor(info, &cursor);
1105 vbl_cursor_cnt = CURSOR_DRAW_DELAY;
1110 static int scrollback_phys_max = 0;
1111 static int scrollback_max = 0;
1112 static int scrollback_current = 0;
1114 int update_var(int con, struct fb_info *info)
1116 if (con == info->currcon)
1117 return fb_pan_display(info, &info->var);
1121 static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc)
1123 struct display *p = &fb_display[vc->vc_num], *t;
1124 struct vc_data **default_mode = vc->vc_display_fg;
1125 int display_fg = (*default_mode)->vc_num;
1126 int rows, cols, charcnt = 256;
1128 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
1129 t = &fb_display[display_fg];
1130 if (!vc->vc_font.data) {
1131 vc->vc_font.data = p->fontdata = t->fontdata;
1132 vc->vc_font.width = (*default_mode)->vc_font.width;
1133 vc->vc_font.height = (*default_mode)->vc_font.height;
1134 p->userfont = t->userfont;
1136 REFCOUNT(p->fontdata)++;
1137 con_copy_unimap(vc->vc_num, display_fg);
1140 charcnt = FNTCHARCNT(p->fontdata);
1142 vc->vc_can_do_color = info->var.bits_per_pixel != 1;
1143 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1144 if (charcnt == 256) {
1145 vc->vc_hi_font_mask = 0;
1147 vc->vc_hi_font_mask = 0x100;
1148 if (vc->vc_can_do_color)
1149 vc->vc_complement_mask <<= 1;
1151 cols = info->var.xres / vc->vc_font.width;
1152 rows = info->var.yres / vc->vc_font.height;
1153 vc_resize(vc->vc_num, cols, rows);
1154 if (CON_IS_VISIBLE(vc)) {
1155 update_screen(vc->vc_num);
1157 int l = fbcon_softback_size / vc->vc_size_row;
1160 softback_end = softback_buf + l *
1163 /* Smaller scrollback makes no sense, and 0
1164 would screw the operation totally */
1169 switch_screen(fg_console);
1172 static __inline__ void ywrap_up(struct vc_data *vc, int count)
1174 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1175 struct display *p = &fb_display[vc->vc_num];
1177 p->yscroll += count;
1178 if (p->yscroll >= p->vrows) /* Deal with wrap */
1179 p->yscroll -= p->vrows;
1180 info->var.xoffset = 0;
1181 info->var.yoffset = p->yscroll * vc->vc_font.height;
1182 info->var.vmode |= FB_VMODE_YWRAP;
1183 update_var(vc->vc_num, info);
1184 scrollback_max += count;
1185 if (scrollback_max > scrollback_phys_max)
1186 scrollback_max = scrollback_phys_max;
1187 scrollback_current = 0;
1190 static __inline__ void ywrap_down(struct vc_data *vc, int count)
1192 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1193 struct display *p = &fb_display[vc->vc_num];
1195 p->yscroll -= count;
1196 if (p->yscroll < 0) /* Deal with wrap */
1197 p->yscroll += p->vrows;
1198 info->var.xoffset = 0;
1199 info->var.yoffset = p->yscroll * vc->vc_font.height;
1200 info->var.vmode |= FB_VMODE_YWRAP;
1201 update_var(vc->vc_num, info);
1202 scrollback_max -= count;
1203 if (scrollback_max < 0)
1205 scrollback_current = 0;
1208 static __inline__ void ypan_up(struct vc_data *vc, int count)
1210 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1211 struct display *p = &fb_display[vc->vc_num];
1213 p->yscroll += count;
1214 if (p->yscroll > p->vrows - vc->vc_rows) {
1215 accel_bmove(vc, info, p->vrows - vc->vc_rows,
1216 0, 0, 0, vc->vc_rows, vc->vc_cols);
1217 p->yscroll -= p->vrows - vc->vc_rows;
1219 info->var.xoffset = 0;
1220 info->var.yoffset = p->yscroll * vc->vc_font.height;
1221 info->var.vmode &= ~FB_VMODE_YWRAP;
1222 update_var(vc->vc_num, info);
1223 accel_clear_margins(vc, info, 1);
1224 scrollback_max += count;
1225 if (scrollback_max > scrollback_phys_max)
1226 scrollback_max = scrollback_phys_max;
1227 scrollback_current = 0;
1230 static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
1232 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1233 struct display *p = &fb_display[vc->vc_num];
1236 p->yscroll += count;
1237 if (p->yscroll > p->vrows - vc->vc_rows) {
1238 p->yscroll -= p->vrows - vc->vc_rows;
1242 info->var.xoffset = 0;
1243 info->var.yoffset = p->yscroll * vc->vc_font.height;
1244 info->var.vmode &= ~FB_VMODE_YWRAP;
1246 fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
1247 update_var(vc->vc_num, info);
1248 accel_clear_margins(vc, info, 1);
1249 scrollback_max += count;
1250 if (scrollback_max > scrollback_phys_max)
1251 scrollback_max = scrollback_phys_max;
1252 scrollback_current = 0;
1255 static __inline__ void ypan_down(struct vc_data *vc, int count)
1257 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1258 struct display *p = &fb_display[vc->vc_num];
1260 p->yscroll -= count;
1261 if (p->yscroll < 0) {
1262 accel_bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
1263 0, vc->vc_rows, vc->vc_cols);
1264 p->yscroll += p->vrows - vc->vc_rows;
1266 info->var.xoffset = 0;
1267 info->var.yoffset = p->yscroll * vc->vc_font.height;
1268 info->var.vmode &= ~FB_VMODE_YWRAP;
1269 update_var(vc->vc_num, info);
1270 accel_clear_margins(vc, info, 1);
1271 scrollback_max -= count;
1272 if (scrollback_max < 0)
1274 scrollback_current = 0;
1277 static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
1279 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1280 struct display *p = &fb_display[vc->vc_num];
1283 p->yscroll -= count;
1284 if (p->yscroll < 0) {
1285 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;
1292 fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
1293 update_var(vc->vc_num, info);
1294 accel_clear_margins(vc, info, 1);
1295 scrollback_max -= count;
1296 if (scrollback_max < 0)
1298 scrollback_current = 0;
1301 static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
1304 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1305 int count = vc->vc_rows;
1306 unsigned short *d, *s;
1310 d = (u16 *) softback_curr;
1311 if (d == (u16 *) softback_in)
1312 d = (u16 *) vc->vc_origin;
1313 n = softback_curr + delta * vc->vc_size_row;
1314 softback_lines -= delta;
1316 if (softback_curr < softback_top && n < softback_buf) {
1317 n += softback_end - softback_buf;
1318 if (n < softback_top) {
1320 (softback_top - n) / vc->vc_size_row;
1323 } else if (softback_curr >= softback_top
1324 && n < softback_top) {
1326 (softback_top - n) / vc->vc_size_row;
1330 if (softback_curr > softback_in && n >= softback_end) {
1331 n += softback_buf - softback_end;
1332 if (n > softback_in) {
1336 } else if (softback_curr <= softback_in && n > softback_in) {
1341 if (n == softback_curr)
1344 s = (u16 *) softback_curr;
1345 if (s == (u16 *) softback_in)
1346 s = (u16 *) vc->vc_origin;
1348 unsigned short *start;
1352 unsigned short attr = 1;
1355 le = advance_row(s, 1);
1358 if (attr != (c & 0xff00)) {
1361 accel_putcs(vc, info, start, s - start,
1362 real_y(p, line), x);
1367 if (c == scr_readw(d)) {
1369 accel_putcs(vc, info, start, s - start,
1370 real_y(p, line), x);
1382 accel_putcs(vc, info, start, s - start,
1383 real_y(p, line), x);
1385 if (d == (u16 *) softback_end)
1386 d = (u16 *) softback_buf;
1387 if (d == (u16 *) softback_in)
1388 d = (u16 *) vc->vc_origin;
1389 if (s == (u16 *) softback_end)
1390 s = (u16 *) softback_buf;
1391 if (s == (u16 *) softback_in)
1392 s = (u16 *) vc->vc_origin;
1396 static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
1397 int line, int count, int dy)
1399 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1400 unsigned short *s = (unsigned short *)
1401 (vc->vc_origin + vc->vc_size_row * line);
1404 unsigned short *start = s;
1405 unsigned short *le = advance_row(s, 1);
1408 unsigned short attr = 1;
1412 if (attr != (c & 0xff00)) {
1415 accel_putcs(vc, info, start, s - start,
1421 console_conditional_schedule();
1425 accel_putcs(vc, info, start, s - start,
1427 console_conditional_schedule();
1432 static void fbcon_redraw(struct vc_data *vc, struct display *p,
1433 int line, int count, int offset)
1435 unsigned short *d = (unsigned short *)
1436 (vc->vc_origin + vc->vc_size_row * line);
1437 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1438 unsigned short *s = d + offset;
1441 unsigned short *start = s;
1442 unsigned short *le = advance_row(s, 1);
1445 unsigned short attr = 1;
1449 if (attr != (c & 0xff00)) {
1452 accel_putcs(vc, info, start, s - start,
1453 real_y(p, line), x);
1458 if (c == scr_readw(d)) {
1460 accel_putcs(vc, info, start, s - start,
1461 real_y(p, line), x);
1470 console_conditional_schedule();
1475 accel_putcs(vc, info, start, s - start,
1476 real_y(p, line), x);
1477 console_conditional_schedule();
1482 /* NOTE: We subtract two lines from these pointers */
1483 s -= vc->vc_size_row;
1484 d -= vc->vc_size_row;
1489 static inline void fbcon_softback_note(struct vc_data *vc, int t,
1494 if (vc->vc_num != fg_console)
1496 p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
1499 scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row);
1501 p = advance_row(p, 1);
1502 softback_in += vc->vc_size_row;
1503 if (softback_in == softback_end)
1504 softback_in = softback_buf;
1505 if (softback_in == softback_top) {
1506 softback_top += vc->vc_size_row;
1507 if (softback_top == softback_end)
1508 softback_top = softback_buf;
1511 softback_curr = softback_in;
1514 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
1517 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1518 struct display *p = &fb_display[vc->vc_num];
1519 int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
1521 if (!info->fbops->fb_blank && console_blanked)
1524 if (!count || vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
1527 fbcon_cursor(vc, CM_ERASE);
1530 * ++Geert: Only use ywrap/ypan if the console is in text mode
1531 * ++Andrew: Only use ypan on hardware text mode when scrolling the
1532 * whole screen (prevents flicker).
1537 if (count > vc->vc_rows) /* Maximum realistic size */
1538 count = vc->vc_rows;
1540 fbcon_softback_note(vc, t, count);
1541 if (logo_shown >= 0)
1543 switch (p->scrollmode) {
1545 accel_bmove(vc, info, t + count, 0, t, 0,
1546 b - t - count, vc->vc_cols);
1547 accel_clear(vc, info, b - count, 0, count,
1551 case SCROLL_WRAP_MOVE:
1552 if (b - t - count > 3 * vc->vc_rows >> 2) {
1554 fbcon_bmove(vc, 0, 0, count, 0, t,
1556 ywrap_up(vc, count);
1557 if (vc->vc_rows - b > 0)
1558 fbcon_bmove(vc, b - count, 0, b, 0,
1561 } else if (info->flags & FBINFO_READS_FAST)
1562 fbcon_bmove(vc, t + count, 0, t, 0,
1563 b - t - count, vc->vc_cols);
1566 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1569 case SCROLL_PAN_REDRAW:
1570 if ((p->yscroll + count <=
1571 2 * (p->vrows - vc->vc_rows))
1572 && ((!scroll_partial && (b - t == vc->vc_rows))
1575 3 * vc->vc_rows >> 2)))) {
1577 fbcon_redraw_move(vc, p, 0, t, count);
1578 ypan_up_redraw(vc, t, count);
1579 if (vc->vc_rows - b > 0)
1580 fbcon_redraw_move(vc, p, b - count,
1581 vc->vc_rows - b, b);
1583 fbcon_redraw_move(vc, p, t + count, b - t - count, t);
1584 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1587 case SCROLL_PAN_MOVE:
1588 if ((p->yscroll + count <=
1589 2 * (p->vrows - vc->vc_rows))
1590 && ((!scroll_partial && (b - t == vc->vc_rows))
1593 3 * vc->vc_rows >> 2)))) {
1595 fbcon_bmove(vc, 0, 0, count, 0, t,
1598 if (vc->vc_rows - b > 0)
1599 fbcon_bmove(vc, b - count, 0, b, 0,
1602 } else if (info->flags & FBINFO_READS_FAST)
1603 fbcon_bmove(vc, t + count, 0, t, 0,
1604 b - t - count, vc->vc_cols);
1607 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1612 fbcon_redraw(vc, p, t, b - t - count,
1613 count * vc->vc_cols);
1614 accel_clear(vc, info, real_y(p, b - count), 0,
1615 count, vc->vc_cols);
1616 scr_memsetw((unsigned short *) (vc->vc_origin +
1619 vc->vc_video_erase_char,
1620 vc->vc_size_row * count);
1626 if (count > vc->vc_rows) /* Maximum realistic size */
1627 count = vc->vc_rows;
1628 switch (p->scrollmode) {
1630 accel_bmove(vc, info, t, 0, t + count, 0,
1631 b - t - count, vc->vc_cols);
1632 accel_clear(vc, info, t, 0, count, vc->vc_cols);
1635 case SCROLL_WRAP_MOVE:
1636 if (b - t - count > 3 * vc->vc_rows >> 2) {
1637 if (vc->vc_rows - b > 0)
1638 fbcon_bmove(vc, b, 0, b - count, 0,
1641 ywrap_down(vc, count);
1643 fbcon_bmove(vc, count, 0, 0, 0, t,
1645 } else if (info->flags & FBINFO_READS_FAST)
1646 fbcon_bmove(vc, t, 0, t + count, 0,
1647 b - t - count, vc->vc_cols);
1650 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1653 case SCROLL_PAN_MOVE:
1654 if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1655 && ((!scroll_partial && (b - t == vc->vc_rows))
1658 3 * vc->vc_rows >> 2)))) {
1659 if (vc->vc_rows - b > 0)
1660 fbcon_bmove(vc, b, 0, b - count, 0,
1663 ypan_down(vc, count);
1665 fbcon_bmove(vc, count, 0, 0, 0, t,
1667 } else if (info->flags & FBINFO_READS_FAST)
1668 fbcon_bmove(vc, t, 0, t + count, 0,
1669 b - t - count, vc->vc_cols);
1672 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1675 case SCROLL_PAN_REDRAW:
1676 if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1677 && ((!scroll_partial && (b - t == vc->vc_rows))
1680 3 * vc->vc_rows >> 2)))) {
1681 if (vc->vc_rows - b > 0)
1682 fbcon_redraw_move(vc, p, b, vc->vc_rows - b,
1684 ypan_down_redraw(vc, t, count);
1686 fbcon_redraw_move(vc, p, count, t, 0);
1688 fbcon_redraw_move(vc, p, t, b - t - count, t + count);
1689 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1694 fbcon_redraw(vc, p, b - 1, b - t - count,
1695 -count * vc->vc_cols);
1696 accel_clear(vc, info, real_y(p, t), 0, count,
1698 scr_memsetw((unsigned short *) (vc->vc_origin +
1701 vc->vc_video_erase_char,
1702 vc->vc_size_row * count);
1710 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
1711 int height, int width)
1713 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1714 struct display *p = &fb_display[vc->vc_num];
1716 if (!info->fbops->fb_blank && console_blanked)
1719 if (!width || !height)
1722 /* Split blits that cross physical y_wrap case.
1723 * Pathological case involves 4 blits, better to use recursive
1724 * code rather than unrolled case
1726 * Recursive invocations don't need to erase the cursor over and
1727 * over again, so we use fbcon_bmove_rec()
1729 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
1730 p->vrows - p->yscroll);
1733 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
1734 int dy, int dx, int height, int width, u_int y_break)
1736 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1739 if (sy < y_break && sy + height > y_break) {
1741 if (dy < sy) { /* Avoid trashing self */
1742 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1744 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1745 height - b, width, y_break);
1747 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1748 height - b, width, y_break);
1749 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1755 if (dy < y_break && dy + height > y_break) {
1757 if (dy < sy) { /* Avoid trashing self */
1758 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1760 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1761 height - b, width, y_break);
1763 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1764 height - b, width, y_break);
1765 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1770 accel_bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
1774 static __inline__ void updatescrollmode(struct display *p, struct fb_info *info,
1777 int fh = vc->vc_font.height;
1778 int cap = info->flags;
1779 int good_pan = (cap & FBINFO_HWACCEL_YPAN)
1780 && divides(info->fix.ypanstep, vc->vc_font.height)
1781 && info->var.yres_virtual > info->var.yres;
1782 int good_wrap = (cap & FBINFO_HWACCEL_YWRAP)
1783 && divides(info->fix.ywrapstep, vc->vc_font.height)
1784 && divides(vc->vc_font.height, info->var.yres_virtual);
1785 int reading_fast = cap & FBINFO_READS_FAST;
1786 int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) && !(cap & FBINFO_HWACCEL_DISABLED);
1787 int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) && !(cap & FBINFO_HWACCEL_DISABLED);
1789 p->vrows = info->var.yres_virtual/fh;
1790 if (info->var.yres > (fh * (vc->vc_rows + 1)))
1791 p->vrows -= (info->var.yres - (fh * vc->vc_rows)) / fh;
1792 if ((info->var.yres % fh) && (info->var.yres_virtual % fh <
1793 info->var.yres % fh))
1796 if (good_wrap || good_pan) {
1797 if (reading_fast || fast_copyarea)
1798 p->scrollmode = good_wrap ? SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
1800 p->scrollmode = good_wrap ? SCROLL_REDRAW :
1803 if (reading_fast || (fast_copyarea && !fast_imageblit))
1804 p->scrollmode = SCROLL_MOVE;
1806 p->scrollmode = SCROLL_REDRAW;
1810 static int fbcon_resize(struct vc_data *vc, unsigned int width,
1811 unsigned int height)
1813 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1814 struct display *p = &fb_display[vc->vc_num];
1815 struct fb_var_screeninfo var = info->var;
1816 int err; int x_diff, y_diff;
1817 int fw = vc->vc_font.width;
1818 int fh = vc->vc_font.height;
1820 var.xres = width * fw;
1821 var.yres = height * fh;
1822 x_diff = info->var.xres - var.xres;
1823 y_diff = info->var.yres - var.yres;
1824 if (x_diff < 0 || x_diff > fw || (y_diff < 0 || y_diff > fh)) {
1827 DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
1828 snprintf(mode, 40, "%ix%i", var.xres, var.yres);
1829 err = fb_find_mode(&var, info, mode, info->monspecs.modedb,
1830 info->monspecs.modedb_len, NULL,
1831 info->var.bits_per_pixel);
1832 if (!err || width > var.xres/fw || height > var.yres/fh)
1834 DPRINTK("resize now %ix%i\n", var.xres, var.yres);
1835 if (CON_IS_VISIBLE(vc)) {
1836 var.activate = FB_ACTIVATE_NOW |
1838 fb_set_var(info, &var);
1839 info->flags &= ~FBINFO_MISC_MODESWITCH;
1842 updatescrollmode(p, info, vc);
1846 static int fbcon_switch(struct vc_data *vc)
1848 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1849 struct display *p = &fb_display[vc->vc_num];
1853 int l = fbcon_softback_size / vc->vc_size_row;
1855 fbcon_set_origin(vc);
1856 softback_top = softback_curr = softback_in = softback_buf;
1860 softback_end = softback_buf + l * vc->vc_size_row;
1862 /* Smaller scrollback makes no sense, and 0 would screw
1863 the operation totally */
1867 if (logo_shown >= 0) {
1868 struct vc_data *conp2 = vc_cons[logo_shown].d;
1870 if (conp2->vc_top == logo_lines
1871 && conp2->vc_bottom == conp2->vc_rows)
1876 info->var.yoffset = p->yscroll = 0;
1879 * FIXME: If we have multiple fbdev's loaded, we need to
1880 * update all info->currcon. Perhaps, we can place this
1881 * in a centralized structure, but this might break some
1884 * info->currcon = vc->vc_num;
1886 for (i = 0; i < FB_MAX; i++) {
1887 if (registered_fb[i] != NULL)
1888 registered_fb[i]->currcon = vc->vc_num;
1891 fbcon_resize(vc, vc->vc_cols, vc->vc_rows);
1893 if (info->flags & FBINFO_MISC_MODESWITCH) {
1894 if (info->fbops->fb_set_par)
1895 info->fbops->fb_set_par(info);
1896 info->flags &= ~FBINFO_MISC_MODESWITCH;
1899 switch (p->scrollmode) {
1900 case SCROLL_WRAP_MOVE:
1901 scrollback_phys_max = p->vrows - vc->vc_rows;
1903 case SCROLL_PAN_MOVE:
1904 case SCROLL_PAN_REDRAW:
1905 scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
1906 if (scrollback_phys_max < 0)
1907 scrollback_phys_max = 0;
1910 scrollback_phys_max = 0;
1914 scrollback_current = 0;
1916 update_var(vc->vc_num, info);
1917 fbcon_set_palette(vc, color_table);
1919 if (vt_cons[vc->vc_num]->vc_mode == KD_TEXT)
1920 accel_clear_margins(vc, info, 0);
1921 if (logo_shown == -2) {
1922 logo_shown = fg_console;
1923 /* This is protected above by initmem_freed */
1925 update_region(fg_console,
1926 vc->vc_origin + vc->vc_size_row * vc->vc_top,
1927 vc->vc_size_row * (vc->vc_bottom -
1934 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
1936 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
1937 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1938 struct display *p = &fb_display[vc->vc_num];
1941 struct fb_var_screeninfo var = info->var;
1944 * HACK ALERT: Some hardware will require reinitializion at this stage,
1945 * others will require it to be done as late as possible.
1946 * For now, we differentiate this with the
1947 * FBINFO_MISC_MODESWITCHLATE bitflag. Worst case will be
1948 * hardware that requires it here and another one later.
1949 * A definitive solution may require fixing X or the VT
1952 if (info->flags & FBINFO_MISC_MODESWITCHLATE)
1953 info->flags |= FBINFO_MISC_MODESWITCH;
1956 fbcon_cursor(vc, CM_ERASE);
1960 if (!(info->flags & FBINFO_MISC_MODESWITCHLATE)) {
1961 var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
1962 fb_set_var(info, &var);
1966 fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
1968 if (!info->fbops->fb_blank) {
1970 unsigned short oldc;
1974 oldc = vc->vc_video_erase_char;
1975 vc->vc_video_erase_char &= charmask;
1976 height = vc->vc_rows;
1977 y_break = p->vrows - p->yscroll;
1978 if (height > y_break) {
1979 accel_clear(vc, info, real_y(p, 0),
1980 0, y_break, vc->vc_cols);
1981 accel_clear(vc, info, real_y(p, y_break),
1982 0, height - y_break,
1985 accel_clear(vc, info, real_y(p, 0),
1986 0, height, vc->vc_cols);
1987 vc->vc_video_erase_char = oldc;
1989 update_screen(vc->vc_num);
1992 return fb_blank(info, blank);
1995 static void fbcon_free_font(struct display *p)
1997 if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
1998 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
2003 static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
2005 u8 *fontdata = vc->vc_font.data;
2006 u8 *data = font->data;
2009 font->width = vc->vc_font.width;
2010 font->height = vc->vc_font.height;
2011 font->charcount = vc->vc_hi_font_mask ? 512 : 256;
2015 if (font->width <= 8) {
2016 j = vc->vc_font.height;
2017 for (i = 0; i < font->charcount; i++) {
2018 memcpy(data, fontdata, j);
2019 memset(data + j, 0, 32 - j);
2023 } else if (font->width <= 16) {
2024 j = vc->vc_font.height * 2;
2025 for (i = 0; i < font->charcount; i++) {
2026 memcpy(data, fontdata, j);
2027 memset(data + j, 0, 64 - j);
2031 } else if (font->width <= 24) {
2032 for (i = 0; i < font->charcount; i++) {
2033 for (j = 0; j < vc->vc_font.height; j++) {
2034 *data++ = fontdata[0];
2035 *data++ = fontdata[1];
2036 *data++ = fontdata[2];
2037 fontdata += sizeof(u32);
2039 memset(data, 0, 3 * (32 - j));
2040 data += 3 * (32 - j);
2043 j = vc->vc_font.height * 4;
2044 for (i = 0; i < font->charcount; i++) {
2045 memcpy(data, fontdata, j);
2046 memset(data + j, 0, 128 - j);
2054 static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
2055 u8 * data, int userfont)
2057 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2058 struct display *p = &fb_display[vc->vc_num];
2061 char *old_data = NULL;
2063 if (CON_IS_VISIBLE(vc) && softback_lines)
2064 fbcon_set_origin(vc);
2066 resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
2068 old_data = vc->vc_font.data;
2070 cnt = FNTCHARCNT(data);
2073 vc->vc_font.data = p->fontdata = data;
2074 if ((p->userfont = userfont))
2076 vc->vc_font.width = w;
2077 vc->vc_font.height = h;
2078 if (vc->vc_hi_font_mask && cnt == 256) {
2079 vc->vc_hi_font_mask = 0;
2080 if (vc->vc_can_do_color) {
2081 vc->vc_complement_mask >>= 1;
2082 vc->vc_s_complement_mask >>= 1;
2085 /* ++Edmund: reorder the attribute bits */
2086 if (vc->vc_can_do_color) {
2087 unsigned short *cp =
2088 (unsigned short *) vc->vc_origin;
2089 int count = vc->vc_screenbuf_size / 2;
2091 for (; count > 0; count--, cp++) {
2093 scr_writew(((c & 0xfe00) >> 1) |
2096 c = vc->vc_video_erase_char;
2097 vc->vc_video_erase_char =
2098 ((c & 0xfe00) >> 1) | (c & 0xff);
2101 } else if (!vc->vc_hi_font_mask && cnt == 512) {
2102 vc->vc_hi_font_mask = 0x100;
2103 if (vc->vc_can_do_color) {
2104 vc->vc_complement_mask <<= 1;
2105 vc->vc_s_complement_mask <<= 1;
2108 /* ++Edmund: reorder the attribute bits */
2110 unsigned short *cp =
2111 (unsigned short *) vc->vc_origin;
2112 int count = vc->vc_screenbuf_size / 2;
2114 for (; count > 0; count--, cp++) {
2115 unsigned short newc;
2117 if (vc->vc_can_do_color)
2119 ((c & 0xff00) << 1) | (c &
2123 scr_writew(newc, cp);
2125 c = vc->vc_video_erase_char;
2126 if (vc->vc_can_do_color) {
2127 vc->vc_video_erase_char =
2128 ((c & 0xff00) << 1) | (c & 0xff);
2131 vc->vc_video_erase_char = c & ~0x100;
2137 /* reset wrap/pan */
2138 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
2139 vc_resize(vc->vc_num, info->var.xres / w, info->var.yres / h);
2140 if (CON_IS_VISIBLE(vc) && softback_buf) {
2141 int l = fbcon_softback_size / vc->vc_size_row;
2144 softback_buf + l * vc->vc_size_row;
2146 /* Smaller scrollback makes no sense, and 0 would screw
2147 the operation totally */
2151 } else if (CON_IS_VISIBLE(vc)
2152 && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
2153 accel_clear_margins(vc, info, 0);
2154 update_screen(vc->vc_num);
2157 if (old_data && (--REFCOUNT(old_data) == 0))
2158 kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
2162 static int fbcon_copy_font(struct vc_data *vc, int con)
2164 struct display *od = &fb_display[con];
2165 struct console_font *f = &vc->vc_font;
2167 if (od->fontdata == f->data)
2168 return 0; /* already the same font... */
2169 return fbcon_do_set_font(vc, f->width, f->height, od->fontdata, od->userfont);
2173 * User asked to set font; we are guaranteed that
2174 * a) width and height are in range 1..32
2175 * b) charcount does not exceed 512
2178 static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags)
2180 unsigned charcount = font->charcount;
2181 int w = font->width;
2182 int h = font->height;
2185 u8 *new_data, *data = font->data, *p;
2187 if (charcount != 256 && charcount != 512)
2198 new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER);
2203 new_data += FONT_EXTRA_WORDS * sizeof(int);
2204 FNTSIZE(new_data) = size;
2205 FNTCHARCNT(new_data) = charcount;
2206 REFCOUNT(new_data) = 0; /* usage counter */
2209 for (i = 0; i < charcount; i++) {
2214 } else if (w <= 16) {
2216 for (i = 0; i < charcount; i++) {
2221 } else if (w <= 24) {
2222 for (i = 0; i < charcount; i++) {
2224 for (j = 0; j < h; j++) {
2230 data += 3 * (32 - h);
2234 for (i = 0; i < charcount; i++) {
2240 /* we can do it in u32 chunks because of charcount is 256 or 512, so
2241 font length must be multiple of 256, at least. And 256 is multiple
2244 while (p > new_data) {
2245 p = (u8 *)((u32 *)p - 1);
2248 FNTSUM(new_data) = k;
2249 /* Check if the same font is on some other console already */
2250 for (i = 0; i < MAX_NR_CONSOLES; i++) {
2251 struct vc_data *tmp = vc_cons[i].d;
2253 if (fb_display[i].userfont &&
2254 fb_display[i].fontdata &&
2255 FNTSUM(fb_display[i].fontdata) == k &&
2256 FNTSIZE(fb_display[i].fontdata) == size &&
2257 tmp->vc_font.width == w &&
2258 !memcmp(fb_display[i].fontdata, new_data, size)) {
2259 kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
2260 new_data = fb_display[i].fontdata;
2264 return fbcon_do_set_font(vc, font->width, font->height, new_data, 1);
2267 static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name)
2269 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2270 struct font_desc *f;
2273 f = get_default_font(info->var.xres, info->var.yres);
2274 else if (!(f = find_font(name)))
2277 font->width = f->width;
2278 font->height = f->height;
2279 return fbcon_do_set_font(vc, f->width, f->height, f->data, 0);
2282 static u16 palette_red[16];
2283 static u16 palette_green[16];
2284 static u16 palette_blue[16];
2286 static struct fb_cmap palette_cmap = {
2287 0, 16, palette_red, palette_green, palette_blue, NULL
2290 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table)
2292 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2296 if (!vc->vc_can_do_color
2297 || (!info->fbops->fb_blank && console_blanked))
2299 for (i = j = 0; i < 16; i++) {
2301 val = vc->vc_palette[j++];
2302 palette_red[k] = (val << 8) | val;
2303 val = vc->vc_palette[j++];
2304 palette_green[k] = (val << 8) | val;
2305 val = vc->vc_palette[j++];
2306 palette_blue[k] = (val << 8) | val;
2308 if (info->var.bits_per_pixel <= 4)
2309 palette_cmap.len = 1 << info->var.bits_per_pixel;
2311 palette_cmap.len = 16;
2312 palette_cmap.start = 0;
2313 return fb_set_cmap(&palette_cmap, info);
2316 static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
2321 if (vc->vc_num != fg_console || !softback_lines)
2322 return (u16 *) (vc->vc_origin + offset);
2323 line = offset / vc->vc_size_row;
2324 if (line >= softback_lines)
2325 return (u16 *) (vc->vc_origin + offset -
2326 softback_lines * vc->vc_size_row);
2327 p = softback_curr + offset;
2328 if (p >= softback_end)
2329 p += softback_buf - softback_end;
2333 static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
2339 if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
2340 unsigned long offset = (pos - vc->vc_origin) / 2;
2342 x = offset % vc->vc_cols;
2343 y = offset / vc->vc_cols;
2344 if (vc->vc_num == fg_console)
2345 y += softback_lines;
2346 ret = pos + (vc->vc_cols - x) * 2;
2347 } else if (vc->vc_num == fg_console && softback_lines) {
2348 unsigned long offset = pos - softback_curr;
2350 if (pos < softback_curr)
2351 offset += softback_end - softback_buf;
2353 x = offset % vc->vc_cols;
2354 y = offset / vc->vc_cols;
2355 ret = pos + (vc->vc_cols - x) * 2;
2356 if (ret == softback_end)
2358 if (ret == softback_in)
2359 ret = vc->vc_origin;
2361 /* Should not happen */
2363 ret = vc->vc_origin;
2372 /* As we might be inside of softback, we may work with non-contiguous buffer,
2373 that's why we have to use a separate routine. */
2374 static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
2377 u16 a = scr_readw(p);
2378 if (!vc->vc_can_do_color)
2380 else if (vc->vc_hi_font_mask == 0x100)
2381 a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
2382 (((a) & 0x0e00) << 4);
2384 a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
2385 (((a) & 0x0700) << 4);
2387 if (p == (u16 *) softback_end)
2388 p = (u16 *) softback_buf;
2389 if (p == (u16 *) softback_in)
2390 p = (u16 *) vc->vc_origin;
2394 static int fbcon_scrolldelta(struct vc_data *vc, int lines)
2396 struct fb_info *info = registered_fb[(int) con2fb_map[fg_console]];
2397 struct display *p = &fb_display[fg_console];
2398 int offset, limit, scrollback_old;
2401 if (vc->vc_num != fg_console)
2403 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT || !lines)
2405 if (logo_shown >= 0) {
2406 struct vc_data *conp2 = vc_cons[logo_shown].d;
2408 if (conp2->vc_top == logo_lines
2409 && conp2->vc_bottom == conp2->vc_rows)
2411 if (logo_shown == vc->vc_num) {
2417 logo_lines * vc->vc_size_row;
2418 for (i = 0; i < logo_lines; i++) {
2419 if (p == softback_top)
2421 if (p == softback_buf)
2423 p -= vc->vc_size_row;
2424 q -= vc->vc_size_row;
2425 scr_memcpyw((u16 *) q, (u16 *) p,
2429 update_region(vc->vc_num, vc->vc_origin,
2430 logo_lines * vc->vc_cols);
2434 fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK);
2435 fbcon_redraw_softback(vc, p, lines);
2436 fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK);
2440 if (!scrollback_phys_max)
2443 scrollback_old = scrollback_current;
2444 scrollback_current -= lines;
2445 if (scrollback_current < 0)
2446 scrollback_current = 0;
2447 else if (scrollback_current > scrollback_max)
2448 scrollback_current = scrollback_max;
2449 if (scrollback_current == scrollback_old)
2452 if (!info->fbops->fb_blank &&
2453 (console_blanked || vt_cons[vc->vc_num]->vc_mode != KD_TEXT
2456 fbcon_cursor(vc, CM_ERASE);
2458 offset = p->yscroll - scrollback_current;
2460 switch (p->scrollmode) {
2461 case SCROLL_WRAP_MOVE:
2462 info->var.vmode |= FB_VMODE_YWRAP;
2464 case SCROLL_PAN_MOVE:
2465 case SCROLL_PAN_REDRAW:
2466 limit -= vc->vc_rows;
2467 info->var.vmode &= ~FB_VMODE_YWRAP;
2472 else if (offset >= limit)
2474 info->var.xoffset = 0;
2475 info->var.yoffset = offset * vc->vc_font.height;
2476 update_var(vc->vc_num, info);
2477 if (!scrollback_current)
2478 fbcon_cursor(vc, CM_DRAW);
2482 static int fbcon_set_origin(struct vc_data *vc)
2484 if (softback_lines && !console_blanked)
2485 fbcon_scrolldelta(vc, softback_lines);
2489 static void fbcon_suspended(struct fb_info *info)
2491 /* Clear cursor, restore saved data */
2492 info->cursor.enable = 0;
2493 info->fbops->fb_cursor(info, &info->cursor);
2496 static void fbcon_resumed(struct fb_info *info)
2500 if (info->currcon < 0)
2502 vc = vc_cons[info->currcon].d;
2504 update_screen(vc->vc_num);
2507 static void fbcon_modechanged(struct fb_info *info)
2509 struct vc_data *vc = vc_cons[info->currcon].d;
2513 if (info->currcon < 0 || vt_cons[info->currcon]->vc_mode !=
2516 p = &fb_display[vc->vc_num];
2518 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
2519 vc->vc_can_do_color = info->var.bits_per_pixel != 1;
2520 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
2522 if (CON_IS_VISIBLE(vc)) {
2523 cols = info->var.xres / vc->vc_font.width;
2524 rows = info->var.yres / vc->vc_font.height;
2525 vc_resize(vc->vc_num, cols, rows);
2526 updatescrollmode(p, info, vc);
2528 scrollback_current = 0;
2529 update_var(vc->vc_num, info);
2530 fbcon_set_palette(vc, color_table);
2531 update_screen(vc->vc_num);
2533 int l = fbcon_softback_size / vc->vc_size_row;
2535 softback_end = softback_buf + l * vc->vc_size_row;
2537 /* Smaller scrollback makes no sense, and 0
2538 would screw the operation totally */
2545 static int fbcon_event_notify(struct notifier_block *self,
2546 unsigned long action, void *data)
2548 struct fb_info *info = (struct fb_info *) data;
2551 case FB_EVENT_SUSPEND:
2552 fbcon_suspended(info);
2554 case FB_EVENT_RESUME:
2555 fbcon_resumed(info);
2557 case FB_EVENT_MODE_CHANGE:
2558 fbcon_modechanged(info);
2566 * The console `switch' structure for the frame buffer based console
2569 const struct consw fb_con = {
2570 .owner = THIS_MODULE,
2571 .con_startup = fbcon_startup,
2572 .con_init = fbcon_init,
2573 .con_deinit = fbcon_deinit,
2574 .con_clear = fbcon_clear,
2575 .con_putc = fbcon_putc,
2576 .con_putcs = fbcon_putcs,
2577 .con_cursor = fbcon_cursor,
2578 .con_scroll = fbcon_scroll,
2579 .con_bmove = fbcon_bmove,
2580 .con_switch = fbcon_switch,
2581 .con_blank = fbcon_blank,
2582 .con_font_set = fbcon_set_font,
2583 .con_font_get = fbcon_get_font,
2584 .con_font_default = fbcon_set_def_font,
2585 .con_font_copy = fbcon_copy_font,
2586 .con_set_palette = fbcon_set_palette,
2587 .con_scrolldelta = fbcon_scrolldelta,
2588 .con_set_origin = fbcon_set_origin,
2589 .con_invert_region = fbcon_invert_region,
2590 .con_screen_pos = fbcon_screen_pos,
2591 .con_getxy = fbcon_getxy,
2592 .con_resize = fbcon_resize,
2595 static struct notifier_block fbcon_event_notifier = {
2596 .notifier_call = fbcon_event_notify,
2598 static int fbcon_event_notifier_registered;
2600 /* can't be __init as it can be called by set_con2fb_map() later */
2601 int fb_console_init(void)
2605 for (i = 0; i < MAX_NR_CONSOLES; i++)
2608 if (!num_registered_fb)
2611 if (info_idx == -1) {
2612 for (i = 0; i < FB_MAX; i++) {
2613 if (registered_fb[i] != NULL) {
2619 for (i = first_fb_vc; i <= last_fb_vc; i++)
2620 con2fb_map[i] = info_idx;
2621 err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
2624 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2629 acquire_console_sem();
2630 if (!fbcon_event_notifier_registered) {
2631 fb_register_client(&fbcon_event_notifier);
2632 fbcon_event_notifier_registered = 1;
2634 release_console_sem();
2640 void __exit fb_console_exit(void)
2642 acquire_console_sem();
2643 if (fbcon_event_notifier_registered) {
2644 fb_unregister_client(&fbcon_event_notifier);
2645 fbcon_event_notifier_registered = 0;
2647 release_console_sem();
2648 give_up_console(&fb_con);
2651 module_init(fb_console_init);
2652 module_exit(fb_console_exit);
2657 * Visible symbols for modules
2660 EXPORT_SYMBOL(fb_display);
2661 EXPORT_SYMBOL(fb_con);
2663 MODULE_LICENSE("GPL");