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 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 #define REFCOUNT(fd) (((int *)(fd))[-1])
120 #define FNTSIZE(fd) (((int *)(fd))[-2])
121 #define FNTCHARCNT(fd) (((int *)(fd))[-3])
122 #define FNTSUM(fd) (((int *)(fd))[-4])
123 #define FONT_EXTRA_WORDS 4
125 #define CM_SOFTBACK (8)
127 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
129 static void fbcon_free_font(struct display *);
130 static int fbcon_set_origin(struct vc_data *);
132 #define CURSOR_DRAW_DELAY (1)
134 /* # VBL ints between cursor state changes */
135 #define ARM_CURSOR_BLINK_RATE (10)
136 #define ATARI_CURSOR_BLINK_RATE (42)
137 #define MAC_CURSOR_BLINK_RATE (32)
138 #define DEFAULT_CURSOR_BLINK_RATE (20)
140 static int vbl_cursor_cnt;
142 #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1)
145 * Interface used by the world
148 static const char *fbcon_startup(void);
149 static void fbcon_init(struct vc_data *vc, int init);
150 static void fbcon_deinit(struct vc_data *vc);
151 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
153 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos);
154 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
155 int count, int ypos, int xpos);
156 static void fbcon_cursor(struct vc_data *vc, int mode);
157 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
159 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
160 int height, int width);
161 static int fbcon_switch(struct vc_data *vc);
162 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch);
163 static int fbcon_font_op(struct vc_data *vc, struct console_font_op *op);
164 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table);
165 static int fbcon_scrolldelta(struct vc_data *vc, int lines);
171 static void fbcon_set_display(struct vc_data *vc, int init, int logo);
172 static __inline__ int real_y(struct display *p, int ypos);
173 static __inline__ void updatescrollmode(struct display *p, struct vc_data *vc);
174 static __inline__ void ywrap_up(struct vc_data *vc, int count);
175 static __inline__ void ywrap_down(struct vc_data *vc, int count);
176 static __inline__ void ypan_up(struct vc_data *vc, int count);
177 static __inline__ void ypan_down(struct vc_data *vc, int count);
178 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
179 int dy, int dx, int height, int width, u_int y_break);
183 * On the Macintoy, there may or may not be a working VBL int. We need to probe
185 static int vbl_detected;
187 static irqreturn_t fb_vbl_detect(int irq, void *dummy, struct pt_regs *fp)
194 static void fb_flashcursor(void *private)
196 struct fb_info *info = (struct fb_info *) private;
198 if (!info || info->state != FBINFO_STATE_RUNNING ||
199 info->cursor.rop == ROP_COPY)
201 acquire_console_sem();
202 info->cursor.enable ^= 1;
203 info->fbops->fb_cursor(info, &info->cursor);
204 release_console_sem();
207 #if (defined(__arm__) && defined(IRQ_VSYNCPULSE)) || defined(CONFIG_ATARI) || defined(CONFIG_MAC)
208 static int cursor_blink_rate;
209 static irqreturn_t fb_vbl_handler(int irq, void *dev_id, struct pt_regs *fp)
211 struct fb_info *info = dev_id;
213 if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
214 schedule_work(&info->queue);
215 vbl_cursor_cnt = cursor_blink_rate;
221 static void cursor_timer_handler(unsigned long dev_addr);
223 static struct timer_list cursor_timer =
224 TIMER_INITIALIZER(cursor_timer_handler, 0, 0);
226 static void cursor_timer_handler(unsigned long dev_addr)
228 struct fb_info *info = (struct fb_info *) dev_addr;
230 schedule_work(&info->queue);
231 mod_timer(&cursor_timer, jiffies + HZ/5);
234 int __init fb_console_setup(char *this_opt)
239 if (!this_opt || !*this_opt)
242 while ((options = strsep(&this_opt, ",")) != NULL) {
243 if (!strncmp(options, "font:", 5)) {
244 for (unit = 0; unit < MAX_NR_CONSOLES; unit++)
245 strcpy(fb_display[unit].fontname,
249 if (!strncmp(options, "scrollback:", 11)) {
252 fbcon_softback_size = simple_strtoul(options, &options, 0);
253 if (*options == 'k' || *options == 'K') {
254 fbcon_softback_size *= 1024;
264 if (!strncmp(options, "map:", 4)) {
267 for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
270 con2fb_map[i] = (options[j++]-'0') % FB_MAX;
275 if (!strncmp(options, "vc:", 3)) {
278 first_fb_vc = simple_strtoul(options, &options, 10) - 1;
281 if (*options++ == '-')
282 last_fb_vc = simple_strtoul(options, &options, 10) - 1;
283 fbcon_is_default = 0;
289 __setup("fbcon=", fb_console_setup);
292 * set_con2fb_map - map console to frame buffer device
293 * @unit: virtual console number to map
294 * @newidx: frame buffer index to map virtual console to
296 * Maps a virtual console @unit to a frame buffer device
299 int set_con2fb_map(int unit, int newidx)
301 struct vc_data *vc = vc_cons[unit].d;
305 con2fb_map[unit] = newidx;
306 fbcon_is_default = (vc->vc_sw == &fb_con) ? 1 : 0;
307 take_over_console(&fb_con, unit, unit, fbcon_is_default);
312 * Accelerated handlers.
314 void accel_bmove(struct vc_data *vc, struct fb_info *info, int sy,
315 int sx, int dy, int dx, int height, int width)
317 struct fb_copyarea area;
319 area.sx = sx * vc->vc_font.width;
320 area.sy = sy * vc->vc_font.height;
321 area.dx = dx * vc->vc_font.width;
322 area.dy = dy * vc->vc_font.height;
323 area.height = height * vc->vc_font.height;
324 area.width = width * vc->vc_font.width;
326 info->fbops->fb_copyarea(info, &area);
329 void accel_clear(struct vc_data *vc, struct fb_info *info, int sy,
330 int sx, int height, int width)
332 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
333 struct fb_fillrect region;
335 region.color = attr_bgcol_ec(bgshift, vc);
336 region.dx = sx * vc->vc_font.width;
337 region.dy = sy * vc->vc_font.height;
338 region.width = width * vc->vc_font.width;
339 region.height = height * vc->vc_font.height;
340 region.rop = ROP_COPY;
342 info->fbops->fb_fillrect(info, ®ion);
345 void accel_putcs(struct vc_data *vc, struct fb_info *info,
346 const unsigned short *s, int count, int yy, int xx)
348 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
349 unsigned int width = (vc->vc_font.width + 7) >> 3;
350 unsigned int cellsize = vc->vc_font.height * width;
351 unsigned int maxcnt = info->pixmap.size/cellsize;
352 unsigned int scan_align = info->pixmap.scan_align - 1;
353 unsigned int buf_align = info->pixmap.buf_align - 1;
354 unsigned int shift_low = 0, mod = vc->vc_font.width % 8;
355 unsigned int shift_high = 8, pitch, cnt, size, k;
356 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
357 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
358 unsigned int idx = vc->vc_font.width >> 3;
359 struct fb_image image;
360 u16 c = scr_readw(s);
361 u8 *src, *dst, *dst0;
363 image.fg_color = attr_fgcol(fgshift, c);
364 image.bg_color = attr_bgcol(bgshift, c);
365 image.dx = xx * vc->vc_font.width;
366 image.dy = yy * vc->vc_font.height;
367 image.height = vc->vc_font.height;
376 image.width = vc->vc_font.width * cnt;
377 pitch = ((image.width + 7) >> 3) + scan_align;
378 pitch &= ~scan_align;
379 size = pitch * image.height + buf_align;
381 dst0 = fb_get_buffer_offset(info, &info->pixmap, size);
384 src = vc->vc_font.data + (scr_readw(s++) & charmask)*cellsize;
388 fb_move_buf_unaligned(info, &info->pixmap, dst, pitch,
389 src, idx, image.height, shift_high,
392 dst0 += (shift_low >= 8) ? width : width - 1;
394 shift_high = 8 - shift_low;
396 fb_move_buf_aligned(info, &info->pixmap, dst, pitch,
397 src, idx, image.height);
401 info->fbops->fb_imageblit(info, &image);
402 image.dx += cnt * vc->vc_font.width;
407 void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
410 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
411 unsigned int cw = vc->vc_font.width;
412 unsigned int ch = vc->vc_font.height;
413 unsigned int rw = info->var.xres - (vc->vc_cols*cw);
414 unsigned int bh = info->var.yres - (vc->vc_rows*ch);
415 unsigned int rs = info->var.xres - rw;
416 unsigned int bs = info->var.yres - bh;
417 struct fb_fillrect region;
419 region.color = attr_bgcol_ec(bgshift, vc);
420 region.rop = ROP_COPY;
422 if (rw && !bottom_only) {
423 region.dx = info->var.xoffset + rs;
426 region.height = info->var.yres_virtual;
427 info->fbops->fb_fillrect(info, ®ion);
431 region.dx = info->var.xoffset;
432 region.dy = info->var.yoffset + bs;
435 info->fbops->fb_fillrect(info, ®ion);
440 * Low Level Operations
442 /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */
443 static const char *fbcon_startup(void)
445 const char *display_desc = "frame buffer device";
446 struct font_desc *font = NULL;
447 struct module *owner;
448 struct fb_info *info;
455 * If num_registered_fb is zero, this is a call for the dummy part.
456 * The frame buffer devices weren't initialized yet.
458 if (!num_registered_fb || done)
462 info = registered_fb[0];
463 if (!info) return NULL;
466 owner = info->fbops->owner;
467 if (!try_module_get(owner))
469 if (info->fbops->fb_open && info->fbops->fb_open(info, 0))
472 if (info->fix.type != FB_TYPE_TEXT) {
473 if (fbcon_softback_size) {
477 kmalloc(fbcon_softback_size,
480 fbcon_softback_size = 0;
486 kfree((void *) softback_buf);
492 softback_in = softback_top = softback_curr =
497 font = get_default_font(info->var.xres, info->var.yres);
499 vc = (struct vc_data *) kmalloc(sizeof(struct vc_data), GFP_ATOMIC);
503 kfree((void *) softback_buf);
507 /* Setup default font */
508 vc->vc_font.data = font->data;
509 vc->vc_font.width = font->width;
510 vc->vc_font.height = font->height;
511 vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */
513 vc->vc_cols = info->var.xres/vc->vc_font.width;
514 vc->vc_rows = info->var.yres/vc->vc_font.height;
516 /* We trust the mode the driver supplies. */
517 if (info->fbops->fb_set_par)
518 info->fbops->fb_set_par(info);
520 DPRINTK("mode: %s\n", info->fix.id);
521 DPRINTK("visual: %d\n", info->fix.visual);
522 DPRINTK("res: %dx%d-%d\n", info->var.xres,
524 info->var.bits_per_pixel);
526 info->display_fg = vc;
530 cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
532 request_irq(IRQ_AUTO_4, fb_vbl_handler,
533 IRQ_TYPE_PRIO, "framebuffer vbl",
536 #endif /* CONFIG_ATARI */
540 * On a Macintoy, the VBL interrupt may or may not be active.
541 * As interrupt based cursor is more reliable and race free, we
542 * probe for VBL interrupts.
547 * Probe for VBL: set temp. handler ...
549 irqres = request_irq(IRQ_MAC_VBL, fb_vbl_detect, 0,
550 "framebuffer vbl", info);
554 * ... and spin for 20 ms ...
556 while (!vbl_detected && ++ct < 1000)
561 ("fbcon_startup: No VBL detected, using timer based cursor.\n");
563 free_irq(IRQ_MAC_VBL, fb_vbl_detect);
567 * interrupt based cursor ok
569 cursor_blink_rate = MAC_CURSOR_BLINK_RATE;
571 request_irq(IRQ_MAC_VBL, fb_vbl_handler, 0,
572 "framebuffer vbl", info);
575 * VBL not detected: fall through, use timer based cursor
580 #endif /* CONFIG_MAC */
582 #if defined(__arm__) && defined(IRQ_VSYNCPULSE)
583 cursor_blink_rate = ARM_CURSOR_BLINK_RATE;
584 irqres = request_irq(IRQ_VSYNCPULSE, fb_vbl_handler, SA_SHIRQ,
585 "framebuffer vbl", info);
587 /* Initialize the work queue. If the driver provides its
588 * own work queue this means it will use something besides
589 * default timer to flash the cursor. */
590 if (!info->queue.func) {
591 INIT_WORK(&info->queue, fb_flashcursor, info);
593 cursor_timer.expires = jiffies + HZ / 5;
594 cursor_timer.data = (unsigned long ) info;
595 add_timer(&cursor_timer);
600 static void fbcon_init(struct vc_data *vc, int init)
602 int unit = vc->vc_num;
603 struct fb_info *info;
605 /* on which frame buffer will we open this console? */
606 info = registered_fb[(int) con2fb_map[unit]];
608 if (info->var.accel_flags)
609 fb_display[unit].scrollmode = SCROLL_YNOMOVE;
611 fb_display[unit].scrollmode = SCROLL_YREDRAW;
612 fbcon_set_display(vc, init, !init);
615 static void fbcon_deinit(struct vc_data *vc)
617 struct display *p = &fb_display[vc->vc_num];
622 static __inline__ void updatescrollmode(struct display *p, struct vc_data *vc)
624 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
627 if (p->scrollmode & __SCROLL_YFIXED)
629 if (divides(info->fix.ywrapstep, vc->vc_font.height) &&
630 divides(vc->vc_font.height, info->var.yres_virtual))
632 else if (divides(info->fix.ypanstep, vc->vc_font.height) &&
633 info->var.yres_virtual >= info->var.yres + vc->vc_font.height)
635 else if (p->scrollmode & __SCROLL_YNOMOVE)
636 m = __SCROLL_YREDRAW;
639 p->scrollmode = (p->scrollmode & ~__SCROLL_YMASK) | m;
642 static void fbcon_set_display(struct vc_data *vc, int init, int logo)
644 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
645 int nr_rows, nr_cols, old_rows, old_cols, i, charcnt = 256;
646 struct display *p = &fb_display[vc->vc_num];
647 unsigned short *save = NULL, *r, *q;
648 struct font_desc *font;
650 if (vc->vc_num != fg_console || (info->flags & FBINFO_FLAG_MODULE) ||
651 (info->fix.type == FB_TYPE_TEXT))
654 info->var.xoffset = info->var.yoffset = p->yscroll = 0; /* reset wrap/pan */
656 for (i = 0; i < MAX_NR_CONSOLES; i++)
657 if (vc && i != vc->vc_num && fb_display[i].fontdata)
661 if (i < MAX_NR_CONSOLES) {
662 struct display *q = &fb_display[i];
663 struct vc_data *tmp = vc_cons[i].d;
665 /* If we are not the first console on this
666 fb, copy the font from that console */
667 vc->vc_font.width = tmp->vc_font.width;
668 vc->vc_font.height = tmp->vc_font.height;
669 vc->vc_font.data = p->fontdata = q->fontdata;
670 p->userfont = q->userfont;
672 REFCOUNT(p->fontdata)++;
673 charcnt = FNTCHARCNT(p->fontdata);
675 con_copy_unimap(vc->vc_num, i);
679 if (!p->fontname[0] || !(font = find_font(p->fontname)))
680 font = get_default_font(info->var.xres,
682 vc->vc_font.width = font->width;
683 vc->vc_font.height = font->height;
684 vc->vc_font.data = p->fontdata = font->data;
687 updatescrollmode(p, vc);
689 old_cols = vc->vc_cols;
690 old_rows = vc->vc_rows;
692 nr_cols = info->var.xres / vc->vc_font.width;
693 nr_rows = info->var.yres / vc->vc_font.height;
696 /* Need to make room for the logo */
700 logo_height = fb_prepare_logo(info);
701 logo_lines = (logo_height + vc->vc_font.height - 1) /
703 q = (unsigned short *) (vc->vc_origin +
704 vc->vc_size_row * old_rows);
705 step = logo_lines * old_cols;
706 for (r = q - logo_lines * old_cols; r < q; r++)
707 if (scr_readw(r) != vc->vc_video_erase_char)
709 if (r != q && nr_rows >= old_rows + logo_lines) {
711 kmalloc(logo_lines * nr_cols * 2, GFP_KERNEL);
715 nr_cols ? old_cols : nr_cols;
716 scr_memsetw(save, vc->vc_video_erase_char,
717 logo_lines * nr_cols * 2);
719 for (cnt = 0; cnt < logo_lines;
721 scr_memcpyw(save + cnt * nr_cols,
727 /* We can scroll screen down */
728 r = q - step - old_cols;
729 for (cnt = old_rows - logo_lines; cnt > 0; cnt--) {
730 scr_memcpyw(r + step, r, vc->vc_size_row);
734 vc->vc_y += logo_lines;
735 vc->vc_pos += logo_lines * vc->vc_size_row;
738 scr_memsetw((unsigned short *) vc->vc_origin,
739 vc->vc_video_erase_char,
740 vc->vc_size_row * logo_lines);
744 * ++guenther: console.c:vc_allocate() relies on initializing
745 * vc_{cols,rows}, but we must not set those if we are only
746 * resizing the console.
749 vc->vc_cols = nr_cols;
750 vc->vc_rows = nr_rows;
752 p->vrows = info->var.yres_virtual / vc->vc_font.height;
753 if(info->var.yres > (vc->vc_font.height * (vc->vc_rows + 1))) {
754 p->vrows -= (info->var.yres - (vc->vc_font.height * vc->vc_rows)) / vc->vc_font.height;
756 if ((info->var.yres % vc->vc_font.height) &&
757 (info->var.yres_virtual % vc->vc_font.height <
758 info->var.yres % vc->vc_font.height))
760 vc->vc_can_do_color = info->var.bits_per_pixel != 1;
761 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
762 if (charcnt == 256) {
763 vc->vc_hi_font_mask = 0;
765 vc->vc_hi_font_mask = 0x100;
766 if (vc->vc_can_do_color)
767 vc->vc_complement_mask <<= 1;
771 if (vc->vc_cols != nr_cols || vc->vc_rows != nr_rows)
772 vc_resize(vc->vc_num, nr_cols, nr_rows);
773 else if (CON_IS_VISIBLE(vc) &&
774 vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
775 accel_clear_margins(vc, info, 0);
776 update_screen(vc->vc_num);
779 q = (unsigned short *) (vc->vc_origin +
782 scr_memcpyw(q, save, logo_lines * nr_cols * 2);
783 vc->vc_y += logo_lines;
784 vc->vc_pos += logo_lines * vc->vc_size_row;
790 if (logo_lines > vc->vc_bottom) {
793 "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
796 vc->vc_top = logo_lines;
800 if (vc->vc_num == fg_console && softback_buf) {
801 int l = fbcon_softback_size / vc->vc_size_row;
803 softback_end = softback_buf + l * vc->vc_size_row;
805 /* Smaller scrollback makes no sense, and 0 would screw
806 the operation totally */
813 /* ====================================================================== */
815 /* fbcon_XXX routines - interface used by the world
817 * This system is now divided into two levels because of complications
818 * caused by hardware scrolling. Top level functions:
820 * fbcon_bmove(), fbcon_clear(), fbcon_putc()
822 * handles y values in range [0, scr_height-1] that correspond to real
823 * screen positions. y_wrap shift means that first line of bitmap may be
824 * anywhere on this display. These functions convert lineoffsets to
825 * bitmap offsets and deal with the wrap-around case by splitting blits.
827 * fbcon_bmove_physical_8() -- These functions fast implementations
828 * fbcon_clear_physical_8() -- of original fbcon_XXX fns.
829 * fbcon_putc_physical_8() -- (font width != 8) may be added later
833 * At the moment fbcon_putc() cannot blit across vertical wrap boundary
834 * Implies should only really hardware scroll in rows. Only reason for
835 * restriction is simplicity & efficiency at the moment.
838 static __inline__ int real_y(struct display *p, int ypos)
843 return ypos < rows ? ypos : ypos - rows;
847 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
850 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
852 struct display *p = &fb_display[vc->vc_num];
855 if (!info->fbops->fb_blank && console_blanked)
857 if (info->state != FBINFO_STATE_RUNNING)
860 if (!height || !width)
863 /* Split blits that cross physical y_wrap boundary */
865 y_break = p->vrows - p->yscroll;
866 if (sy < y_break && sy + height - 1 >= y_break) {
867 u_int b = y_break - sy;
868 accel_clear(vc, info, real_y(p, sy), sx, b, width);
869 accel_clear(vc, info, real_y(p, sy + b), sx, height - b,
872 accel_clear(vc, info, real_y(p, sy), sx, height, width);
875 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
877 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
878 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
879 unsigned int scan_align = info->pixmap.scan_align - 1;
880 unsigned int buf_align = info->pixmap.buf_align - 1;
881 unsigned int width = (vc->vc_font.width + 7) >> 3;
882 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
883 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
884 struct display *p = &fb_display[vc->vc_num];
885 unsigned int size, pitch;
886 struct fb_image image;
889 if (!info->fbops->fb_blank && console_blanked)
891 if (info->state != FBINFO_STATE_RUNNING)
894 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
897 image.dx = xpos * vc->vc_font.width;
898 image.dy = real_y(p, ypos) * vc->vc_font.height;
899 image.width = vc->vc_font.width;
900 image.height = vc->vc_font.height;
901 image.fg_color = attr_fgcol(fgshift, c);
902 image.bg_color = attr_bgcol(bgshift, c);
905 src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * width;
907 pitch = width + scan_align;
908 pitch &= ~scan_align;
909 size = pitch * vc->vc_font.height;
913 dst = fb_get_buffer_offset(info, &info->pixmap, size);
916 fb_move_buf_aligned(info, &info->pixmap, dst, pitch, src, width, image.height);
918 info->fbops->fb_imageblit(info, &image);
921 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
922 int count, int ypos, int xpos)
924 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
925 struct display *p = &fb_display[vc->vc_num];
927 if (!info->fbops->fb_blank && console_blanked)
929 if (info->state != FBINFO_STATE_RUNNING)
932 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
935 accel_putcs(vc, info, s, count, real_y(p, ypos), xpos);
938 static void fbcon_cursor(struct vc_data *vc, int mode)
940 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
941 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
942 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
943 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
944 struct display *p = &fb_display[vc->vc_num];
945 int w = (vc->vc_font.width + 7) >> 3, c;
946 int y = real_y(p, vc->vc_y);
947 struct fb_cursor cursor;
949 if (mode & CM_SOFTBACK) {
950 mode &= ~CM_SOFTBACK;
951 if (softback_lines) {
952 if (y + softback_lines >= vc->vc_rows)
957 } else if (softback_lines)
958 fbcon_set_origin(vc);
960 c = scr_readw((u16 *) vc->vc_pos);
962 cursor.image.data = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
963 cursor.set = FB_CUR_SETCUR;
964 cursor.image.depth = 1;
968 if (info->cursor.rop == ROP_XOR) {
969 info->cursor.enable = 0;
970 info->cursor.rop = ROP_COPY;
971 info->fbops->fb_cursor(info, &cursor);
976 info->cursor.enable = 1;
978 if (info->cursor.image.fg_color != attr_fgcol(fgshift, c) ||
979 info->cursor.image.bg_color != attr_bgcol(bgshift, c)) {
980 cursor.image.fg_color = attr_fgcol(fgshift, c);
981 cursor.image.bg_color = attr_bgcol(bgshift, c);
982 cursor.set |= FB_CUR_SETCMAP;
985 if ((info->cursor.image.dx != (vc->vc_font.width * vc->vc_x)) ||
986 (info->cursor.image.dy != (vc->vc_font.height * y))) {
987 cursor.image.dx = vc->vc_font.width * vc->vc_x;
988 cursor.image.dy = vc->vc_font.height * y;
989 cursor.set |= FB_CUR_SETPOS;
992 if (info->cursor.image.height != vc->vc_font.height ||
993 info->cursor.image.width != vc->vc_font.width) {
994 cursor.image.height = vc->vc_font.height;
995 cursor.image.width = vc->vc_font.width;
996 cursor.set |= FB_CUR_SETSIZE;
999 if (info->cursor.hot.x || info->cursor.hot.y) {
1000 cursor.hot.x = cursor.hot.y = 0;
1001 cursor.set |= FB_CUR_SETHOT;
1004 if ((cursor.set & FB_CUR_SETSIZE) || ((vc->vc_cursor_type & 0x0f) != p->cursor_shape)) {
1005 char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
1006 int cur_height, size, i = 0;
1010 if (info->cursor.mask)
1011 kfree(info->cursor.mask);
1012 info->cursor.mask = mask;
1014 p->cursor_shape = vc->vc_cursor_type & 0x0f;
1015 cursor.set |= FB_CUR_SETSHAPE;
1017 switch (vc->vc_cursor_type & 0x0f) {
1022 cur_height = (vc->vc_font.height < 10) ? 1 : 2;
1024 case CUR_LOWER_THIRD:
1025 cur_height = vc->vc_font.height/3;
1027 case CUR_LOWER_HALF:
1028 cur_height = vc->vc_font.height >> 1;
1030 case CUR_TWO_THIRDS:
1031 cur_height = (vc->vc_font.height << 1)/3;
1035 cur_height = vc->vc_font.height;
1038 size = (vc->vc_font.height - cur_height) * w;
1041 size = cur_height * w;
1045 info->cursor.rop = ROP_XOR;
1046 info->fbops->fb_cursor(info, &cursor);
1047 vbl_cursor_cnt = CURSOR_DRAW_DELAY;
1052 static int scrollback_phys_max = 0;
1053 static int scrollback_max = 0;
1054 static int scrollback_current = 0;
1056 int update_var(int con, struct fb_info *info)
1058 if (con == info->currcon)
1059 return fb_pan_display(info, &info->var);
1063 static __inline__ void ywrap_up(struct vc_data *vc, int count)
1065 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1066 struct display *p = &fb_display[vc->vc_num];
1068 p->yscroll += count;
1069 if (p->yscroll >= p->vrows) /* Deal with wrap */
1070 p->yscroll -= p->vrows;
1071 info->var.xoffset = 0;
1072 info->var.yoffset = p->yscroll * vc->vc_font.height;
1073 info->var.vmode |= FB_VMODE_YWRAP;
1074 update_var(vc->vc_num, info);
1075 scrollback_max += count;
1076 if (scrollback_max > scrollback_phys_max)
1077 scrollback_max = scrollback_phys_max;
1078 scrollback_current = 0;
1081 static __inline__ void ywrap_down(struct vc_data *vc, int count)
1083 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1084 struct display *p = &fb_display[vc->vc_num];
1086 p->yscroll -= count;
1087 if (p->yscroll < 0) /* Deal with wrap */
1088 p->yscroll += p->vrows;
1089 info->var.xoffset = 0;
1090 info->var.yoffset = p->yscroll * vc->vc_font.height;
1091 info->var.vmode |= FB_VMODE_YWRAP;
1092 update_var(vc->vc_num, info);
1093 scrollback_max -= count;
1094 if (scrollback_max < 0)
1096 scrollback_current = 0;
1099 static __inline__ void ypan_up(struct vc_data *vc, int count)
1101 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1102 struct display *p = &fb_display[vc->vc_num];
1104 p->yscroll += count;
1105 if (p->yscroll > p->vrows - vc->vc_rows) {
1106 accel_bmove(vc, info, p->vrows - vc->vc_rows,
1107 0, 0, 0, vc->vc_rows, vc->vc_cols);
1108 p->yscroll -= p->vrows - vc->vc_rows;
1110 info->var.xoffset = 0;
1111 info->var.yoffset = p->yscroll * vc->vc_font.height;
1112 info->var.vmode &= ~FB_VMODE_YWRAP;
1113 update_var(vc->vc_num, info);
1114 accel_clear_margins(vc, info, 1);
1115 scrollback_max += count;
1116 if (scrollback_max > scrollback_phys_max)
1117 scrollback_max = scrollback_phys_max;
1118 scrollback_current = 0;
1121 static __inline__ void ypan_down(struct vc_data *vc, int count)
1123 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1124 struct display *p = &fb_display[vc->vc_num];
1126 p->yscroll -= count;
1127 if (p->yscroll < 0) {
1128 accel_bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
1129 0, vc->vc_rows, vc->vc_cols);
1130 p->yscroll += p->vrows - vc->vc_rows;
1132 info->var.xoffset = 0;
1133 info->var.yoffset = p->yscroll * vc->vc_font.height;
1134 info->var.vmode &= ~FB_VMODE_YWRAP;
1135 update_var(vc->vc_num, info);
1136 accel_clear_margins(vc, info, 1);
1137 scrollback_max -= count;
1138 if (scrollback_max < 0)
1140 scrollback_current = 0;
1143 static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
1146 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1147 int count = vc->vc_rows;
1148 unsigned short *d, *s;
1152 d = (u16 *) softback_curr;
1153 if (d == (u16 *) softback_in)
1154 d = (u16 *) vc->vc_origin;
1155 n = softback_curr + delta * vc->vc_size_row;
1156 softback_lines -= delta;
1158 if (softback_curr < softback_top && n < softback_buf) {
1159 n += softback_end - softback_buf;
1160 if (n < softback_top) {
1162 (softback_top - n) / vc->vc_size_row;
1165 } else if (softback_curr >= softback_top
1166 && n < softback_top) {
1168 (softback_top - n) / vc->vc_size_row;
1172 if (softback_curr > softback_in && n >= softback_end) {
1173 n += softback_buf - softback_end;
1174 if (n > softback_in) {
1178 } else if (softback_curr <= softback_in && n > softback_in) {
1183 if (n == softback_curr)
1186 s = (u16 *) softback_curr;
1187 if (s == (u16 *) softback_in)
1188 s = (u16 *) vc->vc_origin;
1190 unsigned short *start;
1194 unsigned short attr = 1;
1197 le = advance_row(s, 1);
1200 if (attr != (c & 0xff00)) {
1203 accel_putcs(vc, info, start, s - start,
1204 real_y(p, line), x);
1209 if (c == scr_readw(d)) {
1211 accel_putcs(vc, info, start, s - start,
1212 real_y(p, line), x);
1224 accel_putcs(vc, info, start, s - start,
1225 real_y(p, line), x);
1227 if (d == (u16 *) softback_end)
1228 d = (u16 *) softback_buf;
1229 if (d == (u16 *) softback_in)
1230 d = (u16 *) vc->vc_origin;
1231 if (s == (u16 *) softback_end)
1232 s = (u16 *) softback_buf;
1233 if (s == (u16 *) softback_in)
1234 s = (u16 *) vc->vc_origin;
1238 static void fbcon_redraw(struct vc_data *vc, struct display *p,
1239 int line, int count, int offset)
1241 unsigned short *d = (unsigned short *)
1242 (vc->vc_origin + vc->vc_size_row * line);
1243 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1244 unsigned short *s = d + offset;
1247 unsigned short *start = s;
1248 unsigned short *le = advance_row(s, 1);
1251 unsigned short attr = 1;
1255 if (attr != (c & 0xff00)) {
1258 accel_putcs(vc, info, start, s - start,
1259 real_y(p, line), x);
1264 if (c == scr_readw(d)) {
1266 accel_putcs(vc, info, start, s - start,
1267 real_y(p, line), x);
1276 console_conditional_schedule();
1281 accel_putcs(vc, info, start, s - start,
1282 real_y(p, line), x);
1283 console_conditional_schedule();
1288 /* NOTE: We subtract two lines from these pointers */
1289 s -= vc->vc_size_row;
1290 d -= vc->vc_size_row;
1295 static inline void fbcon_softback_note(struct vc_data *vc, int t,
1300 if (vc->vc_num != fg_console)
1302 p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
1305 scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row);
1307 p = advance_row(p, 1);
1308 softback_in += vc->vc_size_row;
1309 if (softback_in == softback_end)
1310 softback_in = softback_buf;
1311 if (softback_in == softback_top) {
1312 softback_top += vc->vc_size_row;
1313 if (softback_top == softback_end)
1314 softback_top = softback_buf;
1317 softback_curr = softback_in;
1320 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
1323 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1324 struct display *p = &fb_display[vc->vc_num];
1325 int scroll_partial = !(p->scrollmode & __SCROLL_YNOPARTIAL);
1327 if (!info->fbops->fb_blank && console_blanked)
1330 if (!count || vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
1333 fbcon_cursor(vc, CM_ERASE);
1336 * ++Geert: Only use ywrap/ypan if the console is in text mode
1337 * ++Andrew: Only use ypan on hardware text mode when scrolling the
1338 * whole screen (prevents flicker).
1343 if (count > vc->vc_rows) /* Maximum realistic size */
1344 count = vc->vc_rows;
1346 fbcon_softback_note(vc, t, count);
1347 if (logo_shown >= 0)
1349 switch (p->scrollmode & __SCROLL_YMASK) {
1350 case __SCROLL_YMOVE:
1351 accel_bmove(vc, info, t + count, 0, t, 0,
1352 b - t - count, vc->vc_cols);
1353 accel_clear(vc, info, b - count, 0, count,
1357 case __SCROLL_YWRAP:
1358 if (b - t - count > 3 * vc->vc_rows >> 2) {
1360 fbcon_bmove(vc, 0, 0, count, 0, t,
1362 ywrap_up(vc, count);
1363 if (vc->vc_rows - b > 0)
1364 fbcon_bmove(vc, b - count, 0, b, 0,
1367 } else if (p->scrollmode & __SCROLL_YPANREDRAW)
1370 fbcon_bmove(vc, t + count, 0, t, 0,
1371 b - t - count, vc->vc_cols);
1372 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1376 if ((p->yscroll + count <=
1377 2 * (p->vrows - vc->vc_rows))
1378 && ((!scroll_partial && (b - t == vc->vc_rows))
1381 3 * vc->vc_rows >> 2)))) {
1383 fbcon_bmove(vc, 0, 0, count, 0, t,
1386 if (vc->vc_rows - b > 0)
1387 fbcon_bmove(vc, b - count, 0, b, 0,
1390 } else if (p->scrollmode & __SCROLL_YPANREDRAW)
1393 fbcon_bmove(vc, t + count, 0, t, 0,
1394 b - t - count, vc->vc_cols);
1395 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1398 case __SCROLL_YREDRAW:
1400 fbcon_redraw(vc, p, t, b - t - count,
1401 count * vc->vc_cols);
1402 accel_clear(vc, info, real_y(p, b - count), 0,
1403 count, vc->vc_cols);
1404 scr_memsetw((unsigned short *) (vc->vc_origin +
1407 vc->vc_video_erase_char,
1408 vc->vc_size_row * count);
1414 if (count > vc->vc_rows) /* Maximum realistic size */
1415 count = vc->vc_rows;
1416 switch (p->scrollmode & __SCROLL_YMASK) {
1417 case __SCROLL_YMOVE:
1418 accel_bmove(vc, info, t, 0, t + count, 0,
1419 b - t - count, vc->vc_cols);
1420 accel_clear(vc, info, t, 0, count, vc->vc_cols);
1423 case __SCROLL_YWRAP:
1424 if (b - t - count > 3 * vc->vc_rows >> 2) {
1425 if (vc->vc_rows - b > 0)
1426 fbcon_bmove(vc, b, 0, b - count, 0,
1429 ywrap_down(vc, count);
1431 fbcon_bmove(vc, count, 0, 0, 0, t,
1433 } else if (p->scrollmode & __SCROLL_YPANREDRAW)
1436 fbcon_bmove(vc, t, 0, t + count, 0,
1437 b - t - count, vc->vc_cols);
1438 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1442 if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1443 && ((!scroll_partial && (b - t == vc->vc_rows))
1446 3 * vc->vc_rows >> 2)))) {
1447 if (vc->vc_rows - b > 0)
1448 fbcon_bmove(vc, b, 0, b - count, 0,
1451 ypan_down(vc, count);
1453 fbcon_bmove(vc, count, 0, 0, 0, t,
1455 } else if (p->scrollmode & __SCROLL_YPANREDRAW)
1458 fbcon_bmove(vc, t, 0, t + count, 0,
1459 b - t - count, vc->vc_cols);
1460 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1463 case __SCROLL_YREDRAW:
1465 fbcon_redraw(vc, p, b - 1, b - t - count,
1466 -count * vc->vc_cols);
1467 accel_clear(vc, info, real_y(p, t), 0, count,
1469 scr_memsetw((unsigned short *) (vc->vc_origin +
1472 vc->vc_video_erase_char,
1473 vc->vc_size_row * count);
1481 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
1482 int height, int width)
1484 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1485 struct display *p = &fb_display[vc->vc_num];
1487 if (!info->fbops->fb_blank && console_blanked)
1490 if (!width || !height)
1493 /* Split blits that cross physical y_wrap case.
1494 * Pathological case involves 4 blits, better to use recursive
1495 * code rather than unrolled case
1497 * Recursive invocations don't need to erase the cursor over and
1498 * over again, so we use fbcon_bmove_rec()
1500 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
1501 p->vrows - p->yscroll);
1504 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
1505 int dy, int dx, int height, int width, u_int y_break)
1507 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1510 if (sy < y_break && sy + height > y_break) {
1512 if (dy < sy) { /* Avoid trashing self */
1513 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1515 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1516 height - b, width, y_break);
1518 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1519 height - b, width, y_break);
1520 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1526 if (dy < y_break && dy + height > y_break) {
1528 if (dy < sy) { /* Avoid trashing self */
1529 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1531 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1532 height - b, width, y_break);
1534 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1535 height - b, width, y_break);
1536 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1541 accel_bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
1545 static int fbcon_resize(struct vc_data *vc, unsigned int width,
1546 unsigned int height)
1548 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1549 struct display *p = &fb_display[vc->vc_num];
1550 struct fb_var_screeninfo var = info->var;
1551 int err; int x_diff, y_diff;
1552 int fw = vc->vc_font.width;
1553 int fh = vc->vc_font.height;
1555 var.xres = width * fw;
1556 var.yres = height * fh;
1557 x_diff = info->var.xres - var.xres;
1558 y_diff = info->var.yres - var.yres;
1559 if (x_diff < 0 || x_diff > fw ||
1560 (y_diff < 0 || y_diff > fh)) {
1561 var.activate = FB_ACTIVATE_TEST;
1562 err = fb_set_var(info, &var);
1563 if (err || width > var.xres/fw ||
1564 height > var.yres/fh)
1566 DPRINTK("resize now %ix%i\n", var.xres, var.yres);
1567 var.activate = FB_ACTIVATE_NOW;
1568 fb_set_var(info, &var);
1570 p->vrows = var.yres_virtual/fh;
1571 if (var.yres > (fh * (height + 1)))
1572 p->vrows -= (var.yres - (fh * height)) / fh;
1576 static int fbcon_switch(struct vc_data *vc)
1578 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1579 struct display *p = &fb_display[vc->vc_num];
1582 int l = fbcon_softback_size / vc->vc_size_row;
1584 fbcon_set_origin(vc);
1585 softback_top = softback_curr = softback_in = softback_buf;
1589 softback_end = softback_buf + l * vc->vc_size_row;
1591 /* Smaller scrollback makes no sense, and 0 would screw
1592 the operation totally */
1596 if (logo_shown >= 0) {
1597 struct vc_data *conp2 = vc_cons[logo_shown].d;
1599 if (conp2->vc_top == logo_lines
1600 && conp2->vc_bottom == conp2->vc_rows)
1605 info->var.yoffset = p->yscroll = 0;
1606 fbcon_resize(vc, vc->vc_cols, vc->vc_rows);
1607 switch (p->scrollmode & __SCROLL_YMASK) {
1608 case __SCROLL_YWRAP:
1609 scrollback_phys_max = p->vrows - vc->vc_rows;
1612 scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
1613 if (scrollback_phys_max < 0)
1614 scrollback_phys_max = 0;
1617 scrollback_phys_max = 0;
1621 scrollback_current = 0;
1623 info->currcon = vc->vc_num;
1625 update_var(vc->vc_num, info);
1626 fbcon_set_palette(vc, color_table);
1628 if (vt_cons[vc->vc_num]->vc_mode == KD_TEXT)
1629 accel_clear_margins(vc, info, 0);
1630 if (logo_shown == -2) {
1631 logo_shown = fg_console;
1632 /* This is protected above by initmem_freed */
1634 update_region(fg_console,
1635 vc->vc_origin + vc->vc_size_row * vc->vc_top,
1636 vc->vc_size_row * (vc->vc_bottom -
1643 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
1645 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
1646 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1647 struct display *p = &fb_display[vc->vc_num];
1650 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1651 struct fb_var_screeninfo var = info->var;
1654 fbcon_cursor(vc, CM_ERASE);
1657 var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
1658 fb_set_var(info, &var);
1661 fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
1663 if (!info->fbops->fb_blank) {
1665 unsigned short oldc;
1669 oldc = vc->vc_video_erase_char;
1670 vc->vc_video_erase_char &= charmask;
1671 height = vc->vc_rows;
1672 y_break = p->vrows - p->yscroll;
1673 if (height > y_break) {
1674 accel_clear(vc, info, real_y(p, 0),
1675 0, y_break, vc->vc_cols);
1676 accel_clear(vc, info, real_y(p, y_break),
1677 0, height - y_break,
1680 accel_clear(vc, info, real_y(p, 0),
1681 0, height, vc->vc_cols);
1682 vc->vc_video_erase_char = oldc;
1684 update_screen(vc->vc_num);
1687 return fb_blank(info, blank);
1690 static void fbcon_free_font(struct display *p)
1692 if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
1693 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
1698 static inline int fbcon_get_font(struct vc_data *vc, struct console_font_op *op)
1700 u8 *fontdata = vc->vc_font.data;
1701 u8 *data = op->data;
1704 op->width = vc->vc_font.width;
1705 op->height = vc->vc_font.height;
1706 op->charcount = vc->vc_hi_font_mask ? 512 : 256;
1710 if (op->width <= 8) {
1711 j = vc->vc_font.height;
1712 for (i = 0; i < op->charcount; i++) {
1713 memcpy(data, fontdata, j);
1714 memset(data + j, 0, 32 - j);
1718 } else if (op->width <= 16) {
1719 j = vc->vc_font.height * 2;
1720 for (i = 0; i < op->charcount; i++) {
1721 memcpy(data, fontdata, j);
1722 memset(data + j, 0, 64 - j);
1726 } else if (op->width <= 24) {
1727 for (i = 0; i < op->charcount; i++) {
1728 for (j = 0; j < vc->vc_font.height; j++) {
1729 *data++ = fontdata[0];
1730 *data++ = fontdata[1];
1731 *data++ = fontdata[2];
1732 fontdata += sizeof(u32);
1734 memset(data, 0, 3 * (32 - j));
1735 data += 3 * (32 - j);
1738 j = vc->vc_font.height * 4;
1739 for (i = 0; i < op->charcount; i++) {
1740 memcpy(data, fontdata, j);
1741 memset(data + j, 0, 128 - j);
1749 static int fbcon_do_set_font(struct vc_data *vc, struct console_font_op *op,
1750 u8 * data, int userfont)
1752 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1753 struct display *p = &fb_display[vc->vc_num];
1758 char *old_data = NULL;
1761 if (userfont && op->op != KD_FONT_OP_COPY)
1762 kfree(data - FONT_EXTRA_WORDS * sizeof(int));
1766 if (CON_IS_VISIBLE(vc) && softback_lines)
1767 fbcon_set_origin(vc);
1769 resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
1771 old_data = vc->vc_font.data;
1773 cnt = FNTCHARCNT(data);
1776 vc->vc_font.data = p->fontdata = data;
1777 if ((p->userfont = userfont))
1779 vc->vc_font.width = w;
1780 vc->vc_font.height = h;
1781 if (vc->vc_hi_font_mask && cnt == 256) {
1782 vc->vc_hi_font_mask = 0;
1783 if (vc->vc_can_do_color) {
1784 vc->vc_complement_mask >>= 1;
1785 vc->vc_s_complement_mask >>= 1;
1788 /* ++Edmund: reorder the attribute bits */
1789 if (vc->vc_can_do_color) {
1790 unsigned short *cp =
1791 (unsigned short *) vc->vc_origin;
1792 int count = vc->vc_screenbuf_size / 2;
1794 for (; count > 0; count--, cp++) {
1796 scr_writew(((c & 0xfe00) >> 1) |
1799 c = vc->vc_video_erase_char;
1800 vc->vc_video_erase_char =
1801 ((c & 0xfe00) >> 1) | (c & 0xff);
1804 } else if (!vc->vc_hi_font_mask && cnt == 512) {
1805 vc->vc_hi_font_mask = 0x100;
1806 if (vc->vc_can_do_color) {
1807 vc->vc_complement_mask <<= 1;
1808 vc->vc_s_complement_mask <<= 1;
1811 /* ++Edmund: reorder the attribute bits */
1813 unsigned short *cp =
1814 (unsigned short *) vc->vc_origin;
1815 int count = vc->vc_screenbuf_size / 2;
1817 for (; count > 0; count--, cp++) {
1818 unsigned short newc;
1820 if (vc->vc_can_do_color)
1822 ((c & 0xff00) << 1) | (c &
1826 scr_writew(newc, cp);
1828 c = vc->vc_video_erase_char;
1829 if (vc->vc_can_do_color) {
1830 vc->vc_video_erase_char =
1831 ((c & 0xff00) << 1) | (c & 0xff);
1834 vc->vc_video_erase_char = c & ~0x100;
1840 /* reset wrap/pan */
1841 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
1842 p->vrows = info->var.yres_virtual / h;
1844 #if 0 /* INCOMPLETE - let the console gurus handle this */
1845 if(info->var.yres > (h * (vc->vc_rows + 1))
1846 p->vrows -= (info->var.yres - (h * vc->vc_rows)) / h;
1848 if ((info->var.yres % h)
1849 && (info->var.yres_virtual % h < info->var.yres % h))
1851 updatescrollmode(p, vc);
1852 vc_resize(vc->vc_num, info->var.xres / w, info->var.yres / h);
1853 if (CON_IS_VISIBLE(vc) && softback_buf) {
1854 int l = fbcon_softback_size / vc->vc_size_row;
1857 softback_buf + l * vc->vc_size_row;
1859 /* Smaller scrollback makes no sense, and 0 would screw
1860 the operation totally */
1864 } else if (CON_IS_VISIBLE(vc)
1865 && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
1866 accel_clear_margins(vc, info, 0);
1867 update_screen(vc->vc_num);
1870 if (old_data && (--REFCOUNT(old_data) == 0))
1871 kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
1875 static inline int fbcon_copy_font(struct vc_data *vc, struct console_font_op *op)
1880 if (h < 0 || !vc_cons_allocated(h))
1882 if (h == vc->vc_num)
1883 return 0; /* nothing to do */
1884 od = &fb_display[h];
1885 if (od->fontdata == vc->vc_font.data)
1886 return 0; /* already the same font... */
1887 op->width = vc->vc_font.width;
1888 op->height = vc->vc_font.height;
1889 return fbcon_do_set_font(vc, op, od->fontdata, od->userfont);
1892 static inline int fbcon_set_font(struct vc_data *vc, struct console_font_op *op)
1898 u8 *new_data, *data = op->data, *p;
1900 if ((w <= 0) || (w > 32)
1901 || (op->charcount != 256 && op->charcount != 512))
1910 size *= op->charcount;
1914 kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER)))
1916 new_data += FONT_EXTRA_WORDS * sizeof(int);
1917 FNTSIZE(new_data) = size;
1918 FNTCHARCNT(new_data) = op->charcount;
1919 REFCOUNT(new_data) = 0; /* usage counter */
1922 for (i = 0; i < op->charcount; i++) {
1927 } else if (w <= 16) {
1929 for (i = 0; i < op->charcount; i++) {
1934 } else if (w <= 24) {
1935 for (i = 0; i < op->charcount; i++) {
1937 for (j = 0; j < h; j++) {
1943 data += 3 * (32 - h);
1947 for (i = 0; i < op->charcount; i++) {
1953 /* we can do it in u32 chunks because of charcount is 256 or 512, so
1954 font length must be multiple of 256, at least. And 256 is multiple
1957 while (p > new_data) {
1958 p = (u8 *)((u32 *)p - 1);
1961 FNTSUM(new_data) = k;
1962 /* Check if the same font is on some other console already */
1963 for (i = 0; i < MAX_NR_CONSOLES; i++) {
1964 struct vc_data *tmp = vc_cons[i].d;
1966 if (fb_display[i].userfont &&
1967 fb_display[i].fontdata &&
1968 FNTSUM(fb_display[i].fontdata) == k &&
1969 FNTSIZE(fb_display[i].fontdata) == size &&
1970 tmp->vc_font.width == w &&
1971 !memcmp(fb_display[i].fontdata, new_data, size)) {
1972 kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
1973 new_data = fb_display[i].fontdata;
1977 return fbcon_do_set_font(vc, op, new_data, 1);
1980 static inline int fbcon_set_def_font(struct vc_data *vc, struct console_font_op *op)
1982 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1983 char name[MAX_FONT_NAME];
1984 struct font_desc *f;
1987 f = get_default_font(info->var.xres, info->var.yres);
1988 else if (strncpy_from_user(name, op->data, MAX_FONT_NAME - 1) < 0)
1991 name[MAX_FONT_NAME - 1] = 0;
1992 if (!(f = find_font(name)))
1995 op->width = f->width;
1996 op->height = f->height;
1997 return fbcon_do_set_font(vc, op, f->data, 0);
2000 static int fbcon_font_op(struct vc_data *vc, struct console_font_op *op)
2003 case KD_FONT_OP_SET:
2004 return fbcon_set_font(vc, op);
2005 case KD_FONT_OP_GET:
2006 return fbcon_get_font(vc, op);
2007 case KD_FONT_OP_SET_DEFAULT:
2008 return fbcon_set_def_font(vc, op);
2009 case KD_FONT_OP_COPY:
2010 return fbcon_copy_font(vc, op);
2016 static u16 palette_red[16];
2017 static u16 palette_green[16];
2018 static u16 palette_blue[16];
2020 static struct fb_cmap palette_cmap = {
2021 0, 16, palette_red, palette_green, palette_blue, NULL
2024 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table)
2026 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
2030 if (!vc->vc_can_do_color
2031 || (!info->fbops->fb_blank && console_blanked))
2033 for (i = j = 0; i < 16; i++) {
2035 val = vc->vc_palette[j++];
2036 palette_red[k] = (val << 8) | val;
2037 val = vc->vc_palette[j++];
2038 palette_green[k] = (val << 8) | val;
2039 val = vc->vc_palette[j++];
2040 palette_blue[k] = (val << 8) | val;
2042 if (info->var.bits_per_pixel <= 4)
2043 palette_cmap.len = 1 << info->var.bits_per_pixel;
2045 palette_cmap.len = 16;
2046 palette_cmap.start = 0;
2047 return fb_set_cmap(&palette_cmap, 1, info);
2050 static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
2055 if (vc->vc_num != fg_console || !softback_lines)
2056 return (u16 *) (vc->vc_origin + offset);
2057 line = offset / vc->vc_size_row;
2058 if (line >= softback_lines)
2059 return (u16 *) (vc->vc_origin + offset -
2060 softback_lines * vc->vc_size_row);
2061 p = softback_curr + offset;
2062 if (p >= softback_end)
2063 p += softback_buf - softback_end;
2067 static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
2073 if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
2074 unsigned long offset = (pos - vc->vc_origin) / 2;
2076 x = offset % vc->vc_cols;
2077 y = offset / vc->vc_cols;
2078 if (vc->vc_num == fg_console)
2079 y += softback_lines;
2080 ret = pos + (vc->vc_cols - x) * 2;
2081 } else if (vc->vc_num == fg_console && softback_lines) {
2082 unsigned long offset = pos - softback_curr;
2084 if (pos < softback_curr)
2085 offset += softback_end - softback_buf;
2087 x = offset % vc->vc_cols;
2088 y = offset / vc->vc_cols;
2089 ret = pos + (vc->vc_cols - x) * 2;
2090 if (ret == softback_end)
2092 if (ret == softback_in)
2093 ret = vc->vc_origin;
2095 /* Should not happen */
2097 ret = vc->vc_origin;
2106 /* As we might be inside of softback, we may work with non-contiguous buffer,
2107 that's why we have to use a separate routine. */
2108 static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
2111 u16 a = scr_readw(p);
2112 if (!vc->vc_can_do_color)
2114 else if (vc->vc_hi_font_mask == 0x100)
2115 a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
2116 (((a) & 0x0e00) << 4);
2118 a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
2119 (((a) & 0x0700) << 4);
2121 if (p == (u16 *) softback_end)
2122 p = (u16 *) softback_buf;
2123 if (p == (u16 *) softback_in)
2124 p = (u16 *) vc->vc_origin;
2128 static int fbcon_scrolldelta(struct vc_data *vc, int lines)
2130 struct fb_info *info = registered_fb[(int) con2fb_map[fg_console]];
2131 struct display *p = &fb_display[fg_console];
2132 int offset, limit, scrollback_old;
2135 if (vc->vc_num != fg_console)
2137 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT || !lines)
2139 if (logo_shown >= 0) {
2140 struct vc_data *conp2 = vc_cons[logo_shown].d;
2142 if (conp2->vc_top == logo_lines
2143 && conp2->vc_bottom == conp2->vc_rows)
2145 if (logo_shown == vc->vc_num) {
2151 logo_lines * vc->vc_size_row;
2152 for (i = 0; i < logo_lines; i++) {
2153 if (p == softback_top)
2155 if (p == softback_buf)
2157 p -= vc->vc_size_row;
2158 q -= vc->vc_size_row;
2159 scr_memcpyw((u16 *) q, (u16 *) p,
2163 update_region(vc->vc_num, vc->vc_origin,
2164 logo_lines * vc->vc_cols);
2168 fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK);
2169 fbcon_redraw_softback(vc, p, lines);
2170 fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK);
2174 if (!scrollback_phys_max)
2177 scrollback_old = scrollback_current;
2178 scrollback_current -= lines;
2179 if (scrollback_current < 0)
2180 scrollback_current = 0;
2181 else if (scrollback_current > scrollback_max)
2182 scrollback_current = scrollback_max;
2183 if (scrollback_current == scrollback_old)
2186 if (!info->fbops->fb_blank &&
2187 (console_blanked || vt_cons[vc->vc_num]->vc_mode != KD_TEXT
2190 fbcon_cursor(vc, CM_ERASE);
2192 offset = p->yscroll - scrollback_current;
2194 switch (p->scrollmode && __SCROLL_YMASK) {
2195 case __SCROLL_YWRAP:
2196 info->var.vmode |= FB_VMODE_YWRAP;
2199 limit -= vc->vc_rows;
2200 info->var.vmode &= ~FB_VMODE_YWRAP;
2205 else if (offset >= limit)
2207 info->var.xoffset = 0;
2208 info->var.yoffset = offset * vc->vc_font.height;
2209 update_var(vc->vc_num, info);
2210 if (!scrollback_current)
2211 fbcon_cursor(vc, CM_DRAW);
2215 static int fbcon_set_origin(struct vc_data *vc)
2217 if (softback_lines && !console_blanked)
2218 fbcon_scrolldelta(vc, softback_lines);
2222 static void fbcon_suspended(struct fb_info *info)
2224 /* Clear cursor, restore saved data */
2225 info->cursor.enable = 0;
2226 info->fbops->fb_cursor(info, &info->cursor);
2229 static void fbcon_resumed(struct fb_info *info)
2233 if (info->currcon < 0)
2235 vc = vc_cons[info->currcon].d;
2237 update_screen(vc->vc_num);
2239 static int fbcon_event_notify(struct notifier_block *self,
2240 unsigned long action, void *data)
2242 struct fb_info *info = (struct fb_info *) data;
2245 case FB_EVENT_SUSPEND:
2246 fbcon_suspended(info);
2248 case FB_EVENT_RESUME:
2249 fbcon_resumed(info);
2256 * The console `switch' structure for the frame buffer based console
2259 const struct consw fb_con = {
2260 .con_startup = fbcon_startup,
2261 .con_init = fbcon_init,
2262 .con_deinit = fbcon_deinit,
2263 .con_clear = fbcon_clear,
2264 .con_putc = fbcon_putc,
2265 .con_putcs = fbcon_putcs,
2266 .con_cursor = fbcon_cursor,
2267 .con_scroll = fbcon_scroll,
2268 .con_bmove = fbcon_bmove,
2269 .con_switch = fbcon_switch,
2270 .con_blank = fbcon_blank,
2271 .con_font_op = fbcon_font_op,
2272 .con_set_palette = fbcon_set_palette,
2273 .con_scrolldelta = fbcon_scrolldelta,
2274 .con_set_origin = fbcon_set_origin,
2275 .con_invert_region = fbcon_invert_region,
2276 .con_screen_pos = fbcon_screen_pos,
2277 .con_getxy = fbcon_getxy,
2278 .con_resize = fbcon_resize,
2281 static struct notifier_block fbcon_event_notifer = {
2282 .notifier_call = fbcon_event_notify,
2285 static int fbcon_event_notifier_registered;
2287 int __init fb_console_init(void)
2289 if (!num_registered_fb)
2292 take_over_console(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default);
2293 acquire_console_sem();
2294 if (!fbcon_event_notifier_registered) {
2295 fb_register_client(&fbcon_event_notifer);
2296 fbcon_event_notifier_registered = 1;
2298 release_console_sem();
2304 void __exit fb_console_exit(void)
2306 acquire_console_sem();
2307 if (fbcon_event_notifier_registered) {
2308 fb_unregister_client(&fbcon_event_notifer);
2309 fbcon_event_notifier_registered = 0;
2311 release_console_sem();
2312 give_up_console(&fb_con);
2315 module_init(fb_console_init);
2316 module_exit(fb_console_exit);
2321 * Visible symbols for modules
2324 EXPORT_SYMBOL(fb_display);
2325 EXPORT_SYMBOL(fb_con);
2327 MODULE_LICENSE("GPL");