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 static char fontname[40];
121 #define REFCOUNT(fd) (((int *)(fd))[-1])
122 #define FNTSIZE(fd) (((int *)(fd))[-2])
123 #define FNTCHARCNT(fd) (((int *)(fd))[-3])
124 #define FNTSUM(fd) (((int *)(fd))[-4])
125 #define FONT_EXTRA_WORDS 4
127 #define CM_SOFTBACK (8)
129 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
131 static void fbcon_free_font(struct display *);
132 static int fbcon_set_origin(struct vc_data *);
134 #define CURSOR_DRAW_DELAY (1)
136 /* # VBL ints between cursor state changes */
137 #define ARM_CURSOR_BLINK_RATE (10)
138 #define ATARI_CURSOR_BLINK_RATE (42)
139 #define MAC_CURSOR_BLINK_RATE (32)
140 #define DEFAULT_CURSOR_BLINK_RATE (20)
142 static int vbl_cursor_cnt;
144 #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1)
147 * Interface used by the world
150 static const char *fbcon_startup(void);
151 static void fbcon_init(struct vc_data *vc, int init);
152 static void fbcon_deinit(struct vc_data *vc);
153 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
155 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos);
156 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
157 int count, int ypos, int xpos);
158 static void fbcon_cursor(struct vc_data *vc, int mode);
159 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
161 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
162 int height, int width);
163 static int fbcon_switch(struct vc_data *vc);
164 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch);
165 static int fbcon_font_op(struct vc_data *vc, struct console_font_op *op);
166 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table);
167 static int fbcon_scrolldelta(struct vc_data *vc, int lines);
173 static __inline__ int real_y(struct display *p, int ypos);
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 strcpy(fontname, options + 5);
246 if (!strncmp(options, "scrollback:", 11)) {
249 fbcon_softback_size = simple_strtoul(options, &options, 0);
250 if (*options == 'k' || *options == 'K') {
251 fbcon_softback_size *= 1024;
261 if (!strncmp(options, "map:", 4)) {
264 for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
267 con2fb_map[i] = (options[j++]-'0') % FB_MAX;
272 if (!strncmp(options, "vc:", 3)) {
275 first_fb_vc = simple_strtoul(options, &options, 10) - 1;
278 if (*options++ == '-')
279 last_fb_vc = simple_strtoul(options, &options, 10) - 1;
280 fbcon_is_default = 0;
286 __setup("fbcon=", fb_console_setup);
289 * set_con2fb_map - map console to frame buffer device
290 * @unit: virtual console number to map
291 * @newidx: frame buffer index to map virtual console to
293 * Maps a virtual console @unit to a frame buffer device
296 int set_con2fb_map(int unit, int newidx)
298 struct vc_data *vc = vc_cons[unit].d;
302 con2fb_map[unit] = newidx;
303 fbcon_is_default = (vc->vc_sw == &fb_con) ? 1 : 0;
304 return take_over_console(&fb_con, unit, unit, fbcon_is_default);
308 * Accelerated handlers.
310 void accel_bmove(struct vc_data *vc, struct fb_info *info, int sy,
311 int sx, int dy, int dx, int height, int width)
313 struct fb_copyarea area;
315 area.sx = sx * vc->vc_font.width;
316 area.sy = sy * vc->vc_font.height;
317 area.dx = dx * vc->vc_font.width;
318 area.dy = dy * vc->vc_font.height;
319 area.height = height * vc->vc_font.height;
320 area.width = width * vc->vc_font.width;
322 info->fbops->fb_copyarea(info, &area);
325 void accel_clear(struct vc_data *vc, struct fb_info *info, int sy,
326 int sx, int height, int width)
328 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
329 struct fb_fillrect region;
331 region.color = attr_bgcol_ec(bgshift, vc);
332 region.dx = sx * vc->vc_font.width;
333 region.dy = sy * vc->vc_font.height;
334 region.width = width * vc->vc_font.width;
335 region.height = height * vc->vc_font.height;
336 region.rop = ROP_COPY;
338 info->fbops->fb_fillrect(info, ®ion);
341 void accel_putcs(struct vc_data *vc, struct fb_info *info,
342 const unsigned short *s, int count, int yy, int xx)
344 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
345 unsigned int width = (vc->vc_font.width + 7) >> 3;
346 unsigned int cellsize = vc->vc_font.height * width;
347 unsigned int maxcnt = info->pixmap.size/cellsize;
348 unsigned int scan_align = info->pixmap.scan_align - 1;
349 unsigned int buf_align = info->pixmap.buf_align - 1;
350 unsigned int shift_low = 0, mod = vc->vc_font.width % 8;
351 unsigned int shift_high = 8, pitch, cnt, size, k;
352 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
353 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
354 unsigned int idx = vc->vc_font.width >> 3;
355 struct fb_image image;
356 u16 c = scr_readw(s);
357 u8 *src, *dst, *dst0;
359 image.fg_color = attr_fgcol(fgshift, c);
360 image.bg_color = attr_bgcol(bgshift, c);
361 image.dx = xx * vc->vc_font.width;
362 image.dy = yy * vc->vc_font.height;
363 image.height = vc->vc_font.height;
372 image.width = vc->vc_font.width * cnt;
373 pitch = ((image.width + 7) >> 3) + scan_align;
374 pitch &= ~scan_align;
375 size = pitch * image.height + buf_align;
377 dst0 = fb_get_buffer_offset(info, &info->pixmap, size);
380 src = vc->vc_font.data + (scr_readw(s++) & charmask)*cellsize;
384 fb_move_buf_unaligned(info, &info->pixmap, dst, pitch,
385 src, idx, image.height, shift_high,
388 dst0 += (shift_low >= 8) ? width : width - 1;
390 shift_high = 8 - shift_low;
392 fb_move_buf_aligned(info, &info->pixmap, dst, pitch,
393 src, idx, image.height);
397 info->fbops->fb_imageblit(info, &image);
398 image.dx += cnt * vc->vc_font.width;
403 void accel_clear_margins(struct vc_data *vc, struct fb_info *info,
406 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
407 unsigned int cw = vc->vc_font.width;
408 unsigned int ch = vc->vc_font.height;
409 unsigned int rw = info->var.xres - (vc->vc_cols*cw);
410 unsigned int bh = info->var.yres - (vc->vc_rows*ch);
411 unsigned int rs = info->var.xres - rw;
412 unsigned int bs = info->var.yres - bh;
413 struct fb_fillrect region;
415 region.color = attr_bgcol_ec(bgshift, vc);
416 region.rop = ROP_COPY;
418 if (rw && !bottom_only) {
419 region.dx = info->var.xoffset + rs;
422 region.height = info->var.yres_virtual;
423 info->fbops->fb_fillrect(info, ®ion);
427 region.dx = info->var.xoffset;
428 region.dy = info->var.yoffset + bs;
431 info->fbops->fb_fillrect(info, ®ion);
436 * Low Level Operations
438 /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */
439 static const char *fbcon_startup(void)
441 const char *display_desc = "frame buffer device";
442 struct display *p = &fb_display[fg_console];
443 struct vc_data *vc = vc_cons[fg_console].d;
444 struct font_desc *font = NULL;
445 struct module *owner;
446 struct fb_info *info;
453 * If num_registered_fb is zero, this is a call for the dummy part.
454 * The frame buffer devices weren't initialized yet.
456 if (!num_registered_fb || done)
460 info = registered_fb[0];
461 if (!info) return NULL;
464 owner = info->fbops->owner;
465 if (!try_module_get(owner))
467 if (info->fbops->fb_open && info->fbops->fb_open(info, 0))
470 if (info->fix.type != FB_TYPE_TEXT) {
471 if (fbcon_softback_size) {
475 kmalloc(fbcon_softback_size,
478 fbcon_softback_size = 0;
484 kfree((void *) softback_buf);
490 softback_in = softback_top = softback_curr =
495 /* Setup default font */
497 if (!fontname[0] || !(font = find_font(fontname)))
498 font = get_default_font(info->var.xres,
500 vc->vc_font.width = font->width;
501 vc->vc_font.height = font->height;
502 vc->vc_font.data = p->fontdata = font->data;
503 vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */
507 * We must always set the mode. The mode of the previous console
508 * driver could be in the same resolution but we are using different
509 * hardware so we have to initialize the hardware.
511 if (info->fbops->fb_set_par)
512 info->fbops->fb_set_par(info);
513 cols = info->var.xres/vc->vc_font.width;
514 rows = info->var.yres/vc->vc_font.height;
515 vc_resize(vc->vc_num, cols, rows);
517 DPRINTK("mode: %s\n", info->fix.id);
518 DPRINTK("visual: %d\n", info->fix.visual);
519 DPRINTK("res: %dx%d-%d\n", info->var.xres,
521 info->var.bits_per_pixel);
522 con_set_default_unimap(vc->vc_num);
526 cursor_blink_rate = ATARI_CURSOR_BLINK_RATE;
528 request_irq(IRQ_AUTO_4, fb_vbl_handler,
529 IRQ_TYPE_PRIO, "framebuffer vbl",
532 #endif /* CONFIG_ATARI */
536 * On a Macintoy, the VBL interrupt may or may not be active.
537 * As interrupt based cursor is more reliable and race free, we
538 * probe for VBL interrupts.
543 * Probe for VBL: set temp. handler ...
545 irqres = request_irq(IRQ_MAC_VBL, fb_vbl_detect, 0,
546 "framebuffer vbl", info);
550 * ... and spin for 20 ms ...
552 while (!vbl_detected && ++ct < 1000)
557 ("fbcon_startup: No VBL detected, using timer based cursor.\n");
559 free_irq(IRQ_MAC_VBL, fb_vbl_detect);
563 * interrupt based cursor ok
565 cursor_blink_rate = MAC_CURSOR_BLINK_RATE;
567 request_irq(IRQ_MAC_VBL, fb_vbl_handler, 0,
568 "framebuffer vbl", info);
571 * VBL not detected: fall through, use timer based cursor
576 #endif /* CONFIG_MAC */
578 #if defined(__arm__) && defined(IRQ_VSYNCPULSE)
579 cursor_blink_rate = ARM_CURSOR_BLINK_RATE;
580 irqres = request_irq(IRQ_VSYNCPULSE, fb_vbl_handler, SA_SHIRQ,
581 "framebuffer vbl", info);
583 /* Initialize the work queue. If the driver provides its
584 * own work queue this means it will use something besides
585 * default timer to flash the cursor. */
586 if (!info->queue.func) {
587 INIT_WORK(&info->queue, fb_flashcursor, info);
589 cursor_timer.expires = jiffies + HZ / 5;
590 cursor_timer.data = (unsigned long ) info;
591 add_timer(&cursor_timer);
596 static void fbcon_init(struct vc_data *vc, int init)
598 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
599 struct vc_data **default_mode = vc->vc_display_fg;
600 struct display *t, *p = &fb_display[vc->vc_num];
601 int display_fg = (*default_mode)->vc_num;
602 int logo = 1, rows, cols, charcnt = 256;
603 unsigned short *save = NULL, *r, *q;
605 if (vc->vc_num != display_fg || (info->flags & FBINFO_FLAG_MODULE) ||
606 (info->fix.type == FB_TYPE_TEXT))
609 info->var.xoffset = info->var.yoffset = p->yscroll = 0; /* reset wrap/pan */
611 /* If we are not the first console on this
612 fb, copy the font from that console */
613 t = &fb_display[display_fg];
614 vc->vc_font.width = (*default_mode)->vc_font.width;
615 vc->vc_font.height = (*default_mode)->vc_font.height;
616 vc->vc_font.data = p->fontdata = t->fontdata;
617 p->userfont = t->userfont;
619 REFCOUNT(p->fontdata)++;
620 charcnt = FNTCHARCNT(p->fontdata);
622 con_copy_unimap(vc->vc_num, display_fg);
624 vc->vc_can_do_color = info->var.bits_per_pixel != 1;
625 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
626 if (charcnt == 256) {
627 vc->vc_hi_font_mask = 0;
629 vc->vc_hi_font_mask = 0x100;
630 if (vc->vc_can_do_color)
631 vc->vc_complement_mask <<= 1;
634 cols = info->var.xres / vc->vc_font.width;
635 rows = info->var.yres / vc->vc_font.height;
636 vc_resize(vc->vc_num, cols, rows);
638 if (info->var.accel_flags)
639 p->scrollmode = SCROLL_YNOMOVE;
641 p->scrollmode = SCROLL_YREDRAW;
644 * ++guenther: console.c:vc_allocate() relies on initializing
645 * vc_{cols,rows}, but we must not set those if we are only
646 * resizing the console.
654 /* Need to make room for the logo */
658 logo_height = fb_prepare_logo(info);
659 logo_lines = (logo_height + vc->vc_font.height - 1) /
661 q = (unsigned short *) (vc->vc_origin +
662 vc->vc_size_row * rows);
663 step = logo_lines * cols;
664 for (r = q - logo_lines * cols; r < q; r++)
665 if (scr_readw(r) != vc->vc_video_erase_char)
667 if (r != q && rows >= rows + logo_lines) {
668 save = kmalloc(logo_lines * cols * 2, GFP_KERNEL);
670 scr_memsetw(save, vc->vc_video_erase_char,
671 logo_lines * cols * 2);
673 for (cnt = 0; cnt < logo_lines; cnt++, r += cols)
674 scr_memcpyw(save + cnt * cols, r, 2 * cols);
679 /* We can scroll screen down */
681 for (cnt = rows - logo_lines; cnt > 0; cnt--) {
682 scr_memcpyw(r + step, r, vc->vc_size_row);
686 vc->vc_y += logo_lines;
687 vc->vc_pos += logo_lines * vc->vc_size_row;
690 scr_memsetw((unsigned short *) vc->vc_origin,
691 vc->vc_video_erase_char,
692 vc->vc_size_row * logo_lines);
694 if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
695 accel_clear_margins(vc, info, 0);
696 update_screen(vc->vc_num);
699 q = (unsigned short *) (vc->vc_origin +
702 scr_memcpyw(q, save, logo_lines * cols * 2);
703 vc->vc_y += logo_lines;
704 vc->vc_pos += logo_lines * vc->vc_size_row;
707 if (logo_lines > vc->vc_bottom) {
710 "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
713 vc->vc_top = logo_lines;
717 if (vc->vc_num == display_fg && softback_buf) {
718 int l = fbcon_softback_size / vc->vc_size_row;
720 softback_end = softback_buf + l * vc->vc_size_row;
722 /* Smaller scrollback makes no sense, and 0 would screw
723 the operation totally */
729 static void fbcon_deinit(struct vc_data *vc)
731 struct display *p = &fb_display[vc->vc_num];
736 /* ====================================================================== */
738 /* fbcon_XXX routines - interface used by the world
740 * This system is now divided into two levels because of complications
741 * caused by hardware scrolling. Top level functions:
743 * fbcon_bmove(), fbcon_clear(), fbcon_putc()
745 * handles y values in range [0, scr_height-1] that correspond to real
746 * screen positions. y_wrap shift means that first line of bitmap may be
747 * anywhere on this display. These functions convert lineoffsets to
748 * bitmap offsets and deal with the wrap-around case by splitting blits.
750 * fbcon_bmove_physical_8() -- These functions fast implementations
751 * fbcon_clear_physical_8() -- of original fbcon_XXX fns.
752 * fbcon_putc_physical_8() -- (font width != 8) may be added later
756 * At the moment fbcon_putc() cannot blit across vertical wrap boundary
757 * Implies should only really hardware scroll in rows. Only reason for
758 * restriction is simplicity & efficiency at the moment.
761 static __inline__ int real_y(struct display *p, int ypos)
766 return ypos < rows ? ypos : ypos - rows;
770 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
773 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
775 struct display *p = &fb_display[vc->vc_num];
778 if (!info->fbops->fb_blank && console_blanked)
780 if (info->state != FBINFO_STATE_RUNNING)
783 if (!height || !width)
786 /* Split blits that cross physical y_wrap boundary */
788 y_break = p->vrows - p->yscroll;
789 if (sy < y_break && sy + height - 1 >= y_break) {
790 u_int b = y_break - sy;
791 accel_clear(vc, info, real_y(p, sy), sx, b, width);
792 accel_clear(vc, info, real_y(p, sy + b), sx, height - b,
795 accel_clear(vc, info, real_y(p, sy), sx, height, width);
798 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
800 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
801 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
802 unsigned int scan_align = info->pixmap.scan_align - 1;
803 unsigned int buf_align = info->pixmap.buf_align - 1;
804 unsigned int width = (vc->vc_font.width + 7) >> 3;
805 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
806 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
807 struct display *p = &fb_display[vc->vc_num];
808 unsigned int size, pitch;
809 struct fb_image image;
812 if (!info->fbops->fb_blank && console_blanked)
814 if (info->state != FBINFO_STATE_RUNNING)
817 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
820 image.dx = xpos * vc->vc_font.width;
821 image.dy = real_y(p, ypos) * vc->vc_font.height;
822 image.width = vc->vc_font.width;
823 image.height = vc->vc_font.height;
824 image.fg_color = attr_fgcol(fgshift, c);
825 image.bg_color = attr_bgcol(bgshift, c);
828 src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * width;
830 pitch = width + scan_align;
831 pitch &= ~scan_align;
832 size = pitch * vc->vc_font.height;
836 dst = fb_get_buffer_offset(info, &info->pixmap, size);
839 fb_move_buf_aligned(info, &info->pixmap, dst, pitch, src, width, image.height);
841 info->fbops->fb_imageblit(info, &image);
844 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
845 int count, int ypos, int xpos)
847 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
848 struct display *p = &fb_display[vc->vc_num];
850 if (!info->fbops->fb_blank && console_blanked)
852 if (info->state != FBINFO_STATE_RUNNING)
855 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
858 accel_putcs(vc, info, s, count, real_y(p, ypos), xpos);
861 static void fbcon_cursor(struct vc_data *vc, int mode)
863 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
864 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
865 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
866 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
867 struct display *p = &fb_display[vc->vc_num];
868 int w = (vc->vc_font.width + 7) >> 3, c;
869 int y = real_y(p, vc->vc_y);
870 struct fb_cursor cursor;
872 if (mode & CM_SOFTBACK) {
873 mode &= ~CM_SOFTBACK;
874 if (softback_lines) {
875 if (y + softback_lines >= vc->vc_rows)
880 } else if (softback_lines)
881 fbcon_set_origin(vc);
883 c = scr_readw((u16 *) vc->vc_pos);
885 cursor.image.data = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
886 cursor.set = FB_CUR_SETCUR;
887 cursor.image.depth = 1;
891 if (info->cursor.rop == ROP_XOR) {
892 info->cursor.enable = 0;
893 info->cursor.rop = ROP_COPY;
894 info->fbops->fb_cursor(info, &cursor);
899 info->cursor.enable = 1;
901 if (info->cursor.image.fg_color != attr_fgcol(fgshift, c) ||
902 info->cursor.image.bg_color != attr_bgcol(bgshift, c)) {
903 cursor.image.fg_color = attr_fgcol(fgshift, c);
904 cursor.image.bg_color = attr_bgcol(bgshift, c);
905 cursor.set |= FB_CUR_SETCMAP;
908 if ((info->cursor.image.dx != (vc->vc_font.width * vc->vc_x)) ||
909 (info->cursor.image.dy != (vc->vc_font.height * y))) {
910 cursor.image.dx = vc->vc_font.width * vc->vc_x;
911 cursor.image.dy = vc->vc_font.height * y;
912 cursor.set |= FB_CUR_SETPOS;
915 if (info->cursor.image.height != vc->vc_font.height ||
916 info->cursor.image.width != vc->vc_font.width) {
917 cursor.image.height = vc->vc_font.height;
918 cursor.image.width = vc->vc_font.width;
919 cursor.set |= FB_CUR_SETSIZE;
922 if (info->cursor.hot.x || info->cursor.hot.y) {
923 cursor.hot.x = cursor.hot.y = 0;
924 cursor.set |= FB_CUR_SETHOT;
927 if ((cursor.set & FB_CUR_SETSIZE) || ((vc->vc_cursor_type & 0x0f) != p->cursor_shape)) {
928 char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
929 int cur_height, size, i = 0;
933 if (info->cursor.mask)
934 kfree(info->cursor.mask);
935 info->cursor.mask = mask;
937 p->cursor_shape = vc->vc_cursor_type & 0x0f;
938 cursor.set |= FB_CUR_SETSHAPE;
940 switch (vc->vc_cursor_type & 0x0f) {
945 cur_height = (vc->vc_font.height < 10) ? 1 : 2;
947 case CUR_LOWER_THIRD:
948 cur_height = vc->vc_font.height/3;
951 cur_height = vc->vc_font.height >> 1;
954 cur_height = (vc->vc_font.height << 1)/3;
958 cur_height = vc->vc_font.height;
961 size = (vc->vc_font.height - cur_height) * w;
964 size = cur_height * w;
968 info->cursor.rop = ROP_XOR;
969 info->fbops->fb_cursor(info, &cursor);
970 vbl_cursor_cnt = CURSOR_DRAW_DELAY;
975 static int scrollback_phys_max = 0;
976 static int scrollback_max = 0;
977 static int scrollback_current = 0;
979 int update_var(int con, struct fb_info *info)
981 if (con == info->currcon)
982 return fb_pan_display(info, &info->var);
986 static __inline__ void ywrap_up(struct vc_data *vc, int count)
988 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
989 struct display *p = &fb_display[vc->vc_num];
992 if (p->yscroll >= p->vrows) /* Deal with wrap */
993 p->yscroll -= p->vrows;
994 info->var.xoffset = 0;
995 info->var.yoffset = p->yscroll * vc->vc_font.height;
996 info->var.vmode |= FB_VMODE_YWRAP;
997 update_var(vc->vc_num, info);
998 scrollback_max += count;
999 if (scrollback_max > scrollback_phys_max)
1000 scrollback_max = scrollback_phys_max;
1001 scrollback_current = 0;
1004 static __inline__ void ywrap_down(struct vc_data *vc, int count)
1006 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1007 struct display *p = &fb_display[vc->vc_num];
1009 p->yscroll -= count;
1010 if (p->yscroll < 0) /* Deal with wrap */
1011 p->yscroll += p->vrows;
1012 info->var.xoffset = 0;
1013 info->var.yoffset = p->yscroll * vc->vc_font.height;
1014 info->var.vmode |= FB_VMODE_YWRAP;
1015 update_var(vc->vc_num, info);
1016 scrollback_max -= count;
1017 if (scrollback_max < 0)
1019 scrollback_current = 0;
1022 static __inline__ void ypan_up(struct vc_data *vc, int count)
1024 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1025 struct display *p = &fb_display[vc->vc_num];
1027 p->yscroll += count;
1028 if (p->yscroll > p->vrows - vc->vc_rows) {
1029 accel_bmove(vc, info, p->vrows - vc->vc_rows,
1030 0, 0, 0, vc->vc_rows, vc->vc_cols);
1031 p->yscroll -= p->vrows - vc->vc_rows;
1033 info->var.xoffset = 0;
1034 info->var.yoffset = p->yscroll * vc->vc_font.height;
1035 info->var.vmode &= ~FB_VMODE_YWRAP;
1036 update_var(vc->vc_num, info);
1037 accel_clear_margins(vc, info, 1);
1038 scrollback_max += count;
1039 if (scrollback_max > scrollback_phys_max)
1040 scrollback_max = scrollback_phys_max;
1041 scrollback_current = 0;
1044 static __inline__ void ypan_down(struct vc_data *vc, int count)
1046 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1047 struct display *p = &fb_display[vc->vc_num];
1049 p->yscroll -= count;
1050 if (p->yscroll < 0) {
1051 accel_bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
1052 0, vc->vc_rows, vc->vc_cols);
1053 p->yscroll += p->vrows - vc->vc_rows;
1055 info->var.xoffset = 0;
1056 info->var.yoffset = p->yscroll * vc->vc_font.height;
1057 info->var.vmode &= ~FB_VMODE_YWRAP;
1058 update_var(vc->vc_num, info);
1059 accel_clear_margins(vc, info, 1);
1060 scrollback_max -= count;
1061 if (scrollback_max < 0)
1063 scrollback_current = 0;
1066 static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
1069 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1070 int count = vc->vc_rows;
1071 unsigned short *d, *s;
1075 d = (u16 *) softback_curr;
1076 if (d == (u16 *) softback_in)
1077 d = (u16 *) vc->vc_origin;
1078 n = softback_curr + delta * vc->vc_size_row;
1079 softback_lines -= delta;
1081 if (softback_curr < softback_top && n < softback_buf) {
1082 n += softback_end - softback_buf;
1083 if (n < softback_top) {
1085 (softback_top - n) / vc->vc_size_row;
1088 } else if (softback_curr >= softback_top
1089 && n < softback_top) {
1091 (softback_top - n) / vc->vc_size_row;
1095 if (softback_curr > softback_in && n >= softback_end) {
1096 n += softback_buf - softback_end;
1097 if (n > softback_in) {
1101 } else if (softback_curr <= softback_in && n > softback_in) {
1106 if (n == softback_curr)
1109 s = (u16 *) softback_curr;
1110 if (s == (u16 *) softback_in)
1111 s = (u16 *) vc->vc_origin;
1113 unsigned short *start;
1117 unsigned short attr = 1;
1120 le = advance_row(s, 1);
1123 if (attr != (c & 0xff00)) {
1126 accel_putcs(vc, info, start, s - start,
1127 real_y(p, line), x);
1132 if (c == scr_readw(d)) {
1134 accel_putcs(vc, info, start, s - start,
1135 real_y(p, line), x);
1147 accel_putcs(vc, info, start, s - start,
1148 real_y(p, line), x);
1150 if (d == (u16 *) softback_end)
1151 d = (u16 *) softback_buf;
1152 if (d == (u16 *) softback_in)
1153 d = (u16 *) vc->vc_origin;
1154 if (s == (u16 *) softback_end)
1155 s = (u16 *) softback_buf;
1156 if (s == (u16 *) softback_in)
1157 s = (u16 *) vc->vc_origin;
1161 static void fbcon_redraw(struct vc_data *vc, struct display *p,
1162 int line, int count, int offset)
1164 unsigned short *d = (unsigned short *)
1165 (vc->vc_origin + vc->vc_size_row * line);
1166 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1167 unsigned short *s = d + offset;
1170 unsigned short *start = s;
1171 unsigned short *le = advance_row(s, 1);
1174 unsigned short attr = 1;
1178 if (attr != (c & 0xff00)) {
1181 accel_putcs(vc, info, start, s - start,
1182 real_y(p, line), x);
1187 if (c == scr_readw(d)) {
1189 accel_putcs(vc, info, start, s - start,
1190 real_y(p, line), x);
1199 console_conditional_schedule();
1204 accel_putcs(vc, info, start, s - start,
1205 real_y(p, line), x);
1206 console_conditional_schedule();
1211 /* NOTE: We subtract two lines from these pointers */
1212 s -= vc->vc_size_row;
1213 d -= vc->vc_size_row;
1218 static inline void fbcon_softback_note(struct vc_data *vc, int t,
1223 if (vc->vc_num != fg_console)
1225 p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
1228 scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row);
1230 p = advance_row(p, 1);
1231 softback_in += vc->vc_size_row;
1232 if (softback_in == softback_end)
1233 softback_in = softback_buf;
1234 if (softback_in == softback_top) {
1235 softback_top += vc->vc_size_row;
1236 if (softback_top == softback_end)
1237 softback_top = softback_buf;
1240 softback_curr = softback_in;
1243 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
1246 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1247 struct display *p = &fb_display[vc->vc_num];
1248 int scroll_partial = !(p->scrollmode & __SCROLL_YNOPARTIAL);
1250 if (!info->fbops->fb_blank && console_blanked)
1253 if (!count || vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
1256 fbcon_cursor(vc, CM_ERASE);
1259 * ++Geert: Only use ywrap/ypan if the console is in text mode
1260 * ++Andrew: Only use ypan on hardware text mode when scrolling the
1261 * whole screen (prevents flicker).
1266 if (count > vc->vc_rows) /* Maximum realistic size */
1267 count = vc->vc_rows;
1269 fbcon_softback_note(vc, t, count);
1270 if (logo_shown >= 0)
1272 switch (p->scrollmode & __SCROLL_YMASK) {
1273 case __SCROLL_YMOVE:
1274 accel_bmove(vc, info, t + count, 0, t, 0,
1275 b - t - count, vc->vc_cols);
1276 accel_clear(vc, info, b - count, 0, count,
1280 case __SCROLL_YWRAP:
1281 if (b - t - count > 3 * vc->vc_rows >> 2) {
1283 fbcon_bmove(vc, 0, 0, count, 0, t,
1285 ywrap_up(vc, count);
1286 if (vc->vc_rows - b > 0)
1287 fbcon_bmove(vc, b - count, 0, b, 0,
1290 } else if (p->scrollmode & __SCROLL_YPANREDRAW)
1293 fbcon_bmove(vc, t + count, 0, t, 0,
1294 b - t - count, vc->vc_cols);
1295 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1299 if ((p->yscroll + count <=
1300 2 * (p->vrows - vc->vc_rows))
1301 && ((!scroll_partial && (b - t == vc->vc_rows))
1304 3 * vc->vc_rows >> 2)))) {
1306 fbcon_bmove(vc, 0, 0, count, 0, t,
1309 if (vc->vc_rows - b > 0)
1310 fbcon_bmove(vc, b - count, 0, b, 0,
1313 } else if (p->scrollmode & __SCROLL_YPANREDRAW)
1316 fbcon_bmove(vc, t + count, 0, t, 0,
1317 b - t - count, vc->vc_cols);
1318 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1321 case __SCROLL_YREDRAW:
1323 fbcon_redraw(vc, p, t, b - t - count,
1324 count * vc->vc_cols);
1325 accel_clear(vc, info, real_y(p, b - count), 0,
1326 count, vc->vc_cols);
1327 scr_memsetw((unsigned short *) (vc->vc_origin +
1330 vc->vc_video_erase_char,
1331 vc->vc_size_row * count);
1337 if (count > vc->vc_rows) /* Maximum realistic size */
1338 count = vc->vc_rows;
1339 switch (p->scrollmode & __SCROLL_YMASK) {
1340 case __SCROLL_YMOVE:
1341 accel_bmove(vc, info, t, 0, t + count, 0,
1342 b - t - count, vc->vc_cols);
1343 accel_clear(vc, info, t, 0, count, vc->vc_cols);
1346 case __SCROLL_YWRAP:
1347 if (b - t - count > 3 * vc->vc_rows >> 2) {
1348 if (vc->vc_rows - b > 0)
1349 fbcon_bmove(vc, b, 0, b - count, 0,
1352 ywrap_down(vc, count);
1354 fbcon_bmove(vc, count, 0, 0, 0, t,
1356 } else if (p->scrollmode & __SCROLL_YPANREDRAW)
1359 fbcon_bmove(vc, t, 0, t + count, 0,
1360 b - t - count, vc->vc_cols);
1361 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1365 if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1366 && ((!scroll_partial && (b - t == vc->vc_rows))
1369 3 * vc->vc_rows >> 2)))) {
1370 if (vc->vc_rows - b > 0)
1371 fbcon_bmove(vc, b, 0, b - count, 0,
1374 ypan_down(vc, count);
1376 fbcon_bmove(vc, count, 0, 0, 0, t,
1378 } else if (p->scrollmode & __SCROLL_YPANREDRAW)
1381 fbcon_bmove(vc, t, 0, t + count, 0,
1382 b - t - count, vc->vc_cols);
1383 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1386 case __SCROLL_YREDRAW:
1388 fbcon_redraw(vc, p, b - 1, b - t - count,
1389 -count * vc->vc_cols);
1390 accel_clear(vc, info, real_y(p, t), 0, count,
1392 scr_memsetw((unsigned short *) (vc->vc_origin +
1395 vc->vc_video_erase_char,
1396 vc->vc_size_row * count);
1404 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
1405 int height, int width)
1407 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1408 struct display *p = &fb_display[vc->vc_num];
1410 if (!info->fbops->fb_blank && console_blanked)
1413 if (!width || !height)
1416 /* Split blits that cross physical y_wrap case.
1417 * Pathological case involves 4 blits, better to use recursive
1418 * code rather than unrolled case
1420 * Recursive invocations don't need to erase the cursor over and
1421 * over again, so we use fbcon_bmove_rec()
1423 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
1424 p->vrows - p->yscroll);
1427 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
1428 int dy, int dx, int height, int width, u_int y_break)
1430 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1433 if (sy < y_break && sy + height > y_break) {
1435 if (dy < sy) { /* Avoid trashing self */
1436 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1438 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1439 height - b, width, y_break);
1441 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1442 height - b, width, y_break);
1443 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1449 if (dy < y_break && dy + height > y_break) {
1451 if (dy < sy) { /* Avoid trashing self */
1452 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1454 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1455 height - b, width, y_break);
1457 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1458 height - b, width, y_break);
1459 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1464 accel_bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
1468 static __inline__ void updatescrollmode(struct display *p, struct fb_info *info, struct vc_data *vc)
1472 if (p->scrollmode & __SCROLL_YFIXED)
1474 if (divides(info->fix.ywrapstep, vc->vc_font.height) &&
1475 divides(vc->vc_font.height, info->var.yres_virtual))
1477 else if (divides(info->fix.ypanstep, vc->vc_font.height) &&
1478 info->var.yres_virtual >= info->var.yres + vc->vc_font.height)
1480 else if (p->scrollmode & __SCROLL_YNOMOVE)
1481 m = __SCROLL_YREDRAW;
1484 p->scrollmode = (p->scrollmode & ~__SCROLL_YMASK) | m;
1487 static int fbcon_resize(struct vc_data *vc, unsigned int width,
1488 unsigned int height)
1490 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1491 struct display *p = &fb_display[vc->vc_num];
1492 struct fb_var_screeninfo var = info->var;
1493 int err; int x_diff, y_diff;
1494 int fw = vc->vc_font.width;
1495 int fh = vc->vc_font.height;
1497 var.xres = width * fw;
1498 var.yres = height * fh;
1499 x_diff = info->var.xres - var.xres;
1500 y_diff = info->var.yres - var.yres;
1501 if (x_diff < 0 || x_diff > fw || (y_diff < 0 || y_diff > fh)) {
1504 DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
1505 if (!info->fbops->fb_set_par)
1508 sprintf(mode, "%dx%d", var.xres, var.yres);
1509 err = fb_find_mode(&var, info, mode, NULL, 0, NULL,
1510 info->var.bits_per_pixel);
1511 if (!err || width > var.xres/fw || height > var.yres/fh)
1513 DPRINTK("resize now %ix%i\n", var.xres, var.yres);
1514 if (CON_IS_VISIBLE(vc)) {
1515 var.activate = FB_ACTIVATE_NOW;
1516 fb_set_var(info, &var);
1519 p->vrows = var.yres_virtual/fh;
1520 if (var.yres > (fh * (height + 1)))
1521 p->vrows -= (var.yres - (fh * height)) / fh;
1522 if ((var.yres % fh) && (var.yres_virtual % fh < var.yres % fh))
1524 updatescrollmode(p, info, vc);
1528 static int fbcon_switch(struct vc_data *vc)
1530 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1531 struct display *p = &fb_display[vc->vc_num];
1534 int l = fbcon_softback_size / vc->vc_size_row;
1536 fbcon_set_origin(vc);
1537 softback_top = softback_curr = softback_in = softback_buf;
1541 softback_end = softback_buf + l * vc->vc_size_row;
1543 /* Smaller scrollback makes no sense, and 0 would screw
1544 the operation totally */
1548 if (logo_shown >= 0) {
1549 struct vc_data *conp2 = vc_cons[logo_shown].d;
1551 if (conp2->vc_top == logo_lines
1552 && conp2->vc_bottom == conp2->vc_rows)
1557 info->var.yoffset = p->yscroll = 0;
1558 fbcon_resize(vc, vc->vc_cols, vc->vc_rows);
1559 switch (p->scrollmode & __SCROLL_YMASK) {
1560 case __SCROLL_YWRAP:
1561 scrollback_phys_max = p->vrows - vc->vc_rows;
1564 scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
1565 if (scrollback_phys_max < 0)
1566 scrollback_phys_max = 0;
1569 scrollback_phys_max = 0;
1573 scrollback_current = 0;
1575 info->currcon = vc->vc_num;
1577 update_var(vc->vc_num, info);
1578 fbcon_set_palette(vc, color_table);
1580 if (vt_cons[vc->vc_num]->vc_mode == KD_TEXT)
1581 accel_clear_margins(vc, info, 0);
1582 if (logo_shown == -2) {
1583 logo_shown = fg_console;
1584 /* This is protected above by initmem_freed */
1586 update_region(fg_console,
1587 vc->vc_origin + vc->vc_size_row * vc->vc_top,
1588 vc->vc_size_row * (vc->vc_bottom -
1595 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
1597 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
1598 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1599 struct display *p = &fb_display[vc->vc_num];
1602 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1603 struct fb_var_screeninfo var = info->var;
1606 fbcon_cursor(vc, CM_ERASE);
1609 var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
1610 fb_set_var(info, &var);
1613 fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
1615 if (!info->fbops->fb_blank) {
1617 unsigned short oldc;
1621 oldc = vc->vc_video_erase_char;
1622 vc->vc_video_erase_char &= charmask;
1623 height = vc->vc_rows;
1624 y_break = p->vrows - p->yscroll;
1625 if (height > y_break) {
1626 accel_clear(vc, info, real_y(p, 0),
1627 0, y_break, vc->vc_cols);
1628 accel_clear(vc, info, real_y(p, y_break),
1629 0, height - y_break,
1632 accel_clear(vc, info, real_y(p, 0),
1633 0, height, vc->vc_cols);
1634 vc->vc_video_erase_char = oldc;
1636 update_screen(vc->vc_num);
1639 return fb_blank(info, blank);
1642 static void fbcon_free_font(struct display *p)
1644 if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
1645 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
1650 static inline int fbcon_get_font(struct vc_data *vc, struct console_font_op *op)
1652 u8 *fontdata = vc->vc_font.data;
1653 u8 *data = op->data;
1656 op->width = vc->vc_font.width;
1657 op->height = vc->vc_font.height;
1658 op->charcount = vc->vc_hi_font_mask ? 512 : 256;
1662 if (op->width <= 8) {
1663 j = vc->vc_font.height;
1664 for (i = 0; i < op->charcount; i++) {
1665 memcpy(data, fontdata, j);
1666 memset(data + j, 0, 32 - j);
1670 } else if (op->width <= 16) {
1671 j = vc->vc_font.height * 2;
1672 for (i = 0; i < op->charcount; i++) {
1673 memcpy(data, fontdata, j);
1674 memset(data + j, 0, 64 - j);
1678 } else if (op->width <= 24) {
1679 for (i = 0; i < op->charcount; i++) {
1680 for (j = 0; j < vc->vc_font.height; j++) {
1681 *data++ = fontdata[0];
1682 *data++ = fontdata[1];
1683 *data++ = fontdata[2];
1684 fontdata += sizeof(u32);
1686 memset(data, 0, 3 * (32 - j));
1687 data += 3 * (32 - j);
1690 j = vc->vc_font.height * 4;
1691 for (i = 0; i < op->charcount; i++) {
1692 memcpy(data, fontdata, j);
1693 memset(data + j, 0, 128 - j);
1701 static int fbcon_do_set_font(struct vc_data *vc, struct console_font_op *op,
1702 u8 * data, int userfont)
1704 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1705 struct display *p = &fb_display[vc->vc_num];
1710 char *old_data = NULL;
1713 if (userfont && op->op != KD_FONT_OP_COPY)
1714 kfree(data - FONT_EXTRA_WORDS * sizeof(int));
1718 if (CON_IS_VISIBLE(vc) && softback_lines)
1719 fbcon_set_origin(vc);
1721 resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
1723 old_data = vc->vc_font.data;
1725 cnt = FNTCHARCNT(data);
1728 vc->vc_font.data = p->fontdata = data;
1729 if ((p->userfont = userfont))
1731 vc->vc_font.width = w;
1732 vc->vc_font.height = h;
1733 if (vc->vc_hi_font_mask && cnt == 256) {
1734 vc->vc_hi_font_mask = 0;
1735 if (vc->vc_can_do_color) {
1736 vc->vc_complement_mask >>= 1;
1737 vc->vc_s_complement_mask >>= 1;
1740 /* ++Edmund: reorder the attribute bits */
1741 if (vc->vc_can_do_color) {
1742 unsigned short *cp =
1743 (unsigned short *) vc->vc_origin;
1744 int count = vc->vc_screenbuf_size / 2;
1746 for (; count > 0; count--, cp++) {
1748 scr_writew(((c & 0xfe00) >> 1) |
1751 c = vc->vc_video_erase_char;
1752 vc->vc_video_erase_char =
1753 ((c & 0xfe00) >> 1) | (c & 0xff);
1756 } else if (!vc->vc_hi_font_mask && cnt == 512) {
1757 vc->vc_hi_font_mask = 0x100;
1758 if (vc->vc_can_do_color) {
1759 vc->vc_complement_mask <<= 1;
1760 vc->vc_s_complement_mask <<= 1;
1763 /* ++Edmund: reorder the attribute bits */
1765 unsigned short *cp =
1766 (unsigned short *) vc->vc_origin;
1767 int count = vc->vc_screenbuf_size / 2;
1769 for (; count > 0; count--, cp++) {
1770 unsigned short newc;
1772 if (vc->vc_can_do_color)
1774 ((c & 0xff00) << 1) | (c &
1778 scr_writew(newc, cp);
1780 c = vc->vc_video_erase_char;
1781 if (vc->vc_can_do_color) {
1782 vc->vc_video_erase_char =
1783 ((c & 0xff00) << 1) | (c & 0xff);
1786 vc->vc_video_erase_char = c & ~0x100;
1792 /* reset wrap/pan */
1793 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
1794 vc_resize(vc->vc_num, info->var.xres / w, info->var.yres / h);
1795 if (CON_IS_VISIBLE(vc) && softback_buf) {
1796 int l = fbcon_softback_size / vc->vc_size_row;
1799 softback_buf + l * vc->vc_size_row;
1801 /* Smaller scrollback makes no sense, and 0 would screw
1802 the operation totally */
1806 } else if (CON_IS_VISIBLE(vc)
1807 && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
1808 accel_clear_margins(vc, info, 0);
1809 update_screen(vc->vc_num);
1812 if (old_data && (--REFCOUNT(old_data) == 0))
1813 kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
1817 static inline int fbcon_copy_font(struct vc_data *vc, struct console_font_op *op)
1822 if (h < 0 || !vc_cons_allocated(h))
1824 if (h == vc->vc_num)
1825 return 0; /* nothing to do */
1826 od = &fb_display[h];
1827 if (od->fontdata == vc->vc_font.data)
1828 return 0; /* already the same font... */
1829 op->width = vc->vc_font.width;
1830 op->height = vc->vc_font.height;
1831 return fbcon_do_set_font(vc, op, od->fontdata, od->userfont);
1834 static inline int fbcon_set_font(struct vc_data *vc, struct console_font_op *op)
1840 u8 *new_data, *data = op->data, *p;
1842 if ((w <= 0) || (w > 32)
1843 || (op->charcount != 256 && op->charcount != 512))
1852 size *= op->charcount;
1856 kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER)))
1858 new_data += FONT_EXTRA_WORDS * sizeof(int);
1859 FNTSIZE(new_data) = size;
1860 FNTCHARCNT(new_data) = op->charcount;
1861 REFCOUNT(new_data) = 0; /* usage counter */
1864 for (i = 0; i < op->charcount; i++) {
1869 } else if (w <= 16) {
1871 for (i = 0; i < op->charcount; i++) {
1876 } else if (w <= 24) {
1877 for (i = 0; i < op->charcount; i++) {
1879 for (j = 0; j < h; j++) {
1885 data += 3 * (32 - h);
1889 for (i = 0; i < op->charcount; i++) {
1895 /* we can do it in u32 chunks because of charcount is 256 or 512, so
1896 font length must be multiple of 256, at least. And 256 is multiple
1899 while (p > new_data) {
1900 p = (u8 *)((u32 *)p - 1);
1903 FNTSUM(new_data) = k;
1904 /* Check if the same font is on some other console already */
1905 for (i = 0; i < MAX_NR_CONSOLES; i++) {
1906 struct vc_data *tmp = vc_cons[i].d;
1908 if (fb_display[i].userfont &&
1909 fb_display[i].fontdata &&
1910 FNTSUM(fb_display[i].fontdata) == k &&
1911 FNTSIZE(fb_display[i].fontdata) == size &&
1912 tmp->vc_font.width == w &&
1913 !memcmp(fb_display[i].fontdata, new_data, size)) {
1914 kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
1915 new_data = fb_display[i].fontdata;
1919 return fbcon_do_set_font(vc, op, new_data, 1);
1922 static inline int fbcon_set_def_font(struct vc_data *vc, struct console_font_op *op)
1924 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1925 char name[MAX_FONT_NAME];
1926 struct font_desc *f;
1929 f = get_default_font(info->var.xres, info->var.yres);
1930 else if (strncpy_from_user(name, op->data, MAX_FONT_NAME - 1) < 0)
1933 name[MAX_FONT_NAME - 1] = 0;
1934 if (!(f = find_font(name)))
1937 op->width = f->width;
1938 op->height = f->height;
1939 return fbcon_do_set_font(vc, op, f->data, 0);
1942 static int fbcon_font_op(struct vc_data *vc, struct console_font_op *op)
1945 case KD_FONT_OP_SET:
1946 return fbcon_set_font(vc, op);
1947 case KD_FONT_OP_GET:
1948 return fbcon_get_font(vc, op);
1949 case KD_FONT_OP_SET_DEFAULT:
1950 return fbcon_set_def_font(vc, op);
1951 case KD_FONT_OP_COPY:
1952 return fbcon_copy_font(vc, op);
1958 static u16 palette_red[16];
1959 static u16 palette_green[16];
1960 static u16 palette_blue[16];
1962 static struct fb_cmap palette_cmap = {
1963 0, 16, palette_red, palette_green, palette_blue, NULL
1966 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table)
1968 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1972 if (!vc->vc_can_do_color
1973 || (!info->fbops->fb_blank && console_blanked))
1975 for (i = j = 0; i < 16; i++) {
1977 val = vc->vc_palette[j++];
1978 palette_red[k] = (val << 8) | val;
1979 val = vc->vc_palette[j++];
1980 palette_green[k] = (val << 8) | val;
1981 val = vc->vc_palette[j++];
1982 palette_blue[k] = (val << 8) | val;
1984 if (info->var.bits_per_pixel <= 4)
1985 palette_cmap.len = 1 << info->var.bits_per_pixel;
1987 palette_cmap.len = 16;
1988 palette_cmap.start = 0;
1989 return fb_set_cmap(&palette_cmap, 1, info);
1992 static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
1997 if (vc->vc_num != fg_console || !softback_lines)
1998 return (u16 *) (vc->vc_origin + offset);
1999 line = offset / vc->vc_size_row;
2000 if (line >= softback_lines)
2001 return (u16 *) (vc->vc_origin + offset -
2002 softback_lines * vc->vc_size_row);
2003 p = softback_curr + offset;
2004 if (p >= softback_end)
2005 p += softback_buf - softback_end;
2009 static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
2015 if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
2016 unsigned long offset = (pos - vc->vc_origin) / 2;
2018 x = offset % vc->vc_cols;
2019 y = offset / vc->vc_cols;
2020 if (vc->vc_num == fg_console)
2021 y += softback_lines;
2022 ret = pos + (vc->vc_cols - x) * 2;
2023 } else if (vc->vc_num == fg_console && softback_lines) {
2024 unsigned long offset = pos - softback_curr;
2026 if (pos < softback_curr)
2027 offset += softback_end - softback_buf;
2029 x = offset % vc->vc_cols;
2030 y = offset / vc->vc_cols;
2031 ret = pos + (vc->vc_cols - x) * 2;
2032 if (ret == softback_end)
2034 if (ret == softback_in)
2035 ret = vc->vc_origin;
2037 /* Should not happen */
2039 ret = vc->vc_origin;
2048 /* As we might be inside of softback, we may work with non-contiguous buffer,
2049 that's why we have to use a separate routine. */
2050 static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
2053 u16 a = scr_readw(p);
2054 if (!vc->vc_can_do_color)
2056 else if (vc->vc_hi_font_mask == 0x100)
2057 a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
2058 (((a) & 0x0e00) << 4);
2060 a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
2061 (((a) & 0x0700) << 4);
2063 if (p == (u16 *) softback_end)
2064 p = (u16 *) softback_buf;
2065 if (p == (u16 *) softback_in)
2066 p = (u16 *) vc->vc_origin;
2070 static int fbcon_scrolldelta(struct vc_data *vc, int lines)
2072 struct fb_info *info = registered_fb[(int) con2fb_map[fg_console]];
2073 struct display *p = &fb_display[fg_console];
2074 int offset, limit, scrollback_old;
2077 if (vc->vc_num != fg_console)
2079 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT || !lines)
2081 if (logo_shown >= 0) {
2082 struct vc_data *conp2 = vc_cons[logo_shown].d;
2084 if (conp2->vc_top == logo_lines
2085 && conp2->vc_bottom == conp2->vc_rows)
2087 if (logo_shown == vc->vc_num) {
2093 logo_lines * vc->vc_size_row;
2094 for (i = 0; i < logo_lines; i++) {
2095 if (p == softback_top)
2097 if (p == softback_buf)
2099 p -= vc->vc_size_row;
2100 q -= vc->vc_size_row;
2101 scr_memcpyw((u16 *) q, (u16 *) p,
2105 update_region(vc->vc_num, vc->vc_origin,
2106 logo_lines * vc->vc_cols);
2110 fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK);
2111 fbcon_redraw_softback(vc, p, lines);
2112 fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK);
2116 if (!scrollback_phys_max)
2119 scrollback_old = scrollback_current;
2120 scrollback_current -= lines;
2121 if (scrollback_current < 0)
2122 scrollback_current = 0;
2123 else if (scrollback_current > scrollback_max)
2124 scrollback_current = scrollback_max;
2125 if (scrollback_current == scrollback_old)
2128 if (!info->fbops->fb_blank &&
2129 (console_blanked || vt_cons[vc->vc_num]->vc_mode != KD_TEXT
2132 fbcon_cursor(vc, CM_ERASE);
2134 offset = p->yscroll - scrollback_current;
2136 switch (p->scrollmode && __SCROLL_YMASK) {
2137 case __SCROLL_YWRAP:
2138 info->var.vmode |= FB_VMODE_YWRAP;
2141 limit -= vc->vc_rows;
2142 info->var.vmode &= ~FB_VMODE_YWRAP;
2147 else if (offset >= limit)
2149 info->var.xoffset = 0;
2150 info->var.yoffset = offset * vc->vc_font.height;
2151 update_var(vc->vc_num, info);
2152 if (!scrollback_current)
2153 fbcon_cursor(vc, CM_DRAW);
2157 static int fbcon_set_origin(struct vc_data *vc)
2159 if (softback_lines && !console_blanked)
2160 fbcon_scrolldelta(vc, softback_lines);
2164 static void fbcon_suspended(struct fb_info *info)
2166 /* Clear cursor, restore saved data */
2167 info->cursor.enable = 0;
2168 info->fbops->fb_cursor(info, &info->cursor);
2171 static void fbcon_resumed(struct fb_info *info)
2175 if (info->currcon < 0)
2177 vc = vc_cons[info->currcon].d;
2179 update_screen(vc->vc_num);
2181 static int fbcon_event_notify(struct notifier_block *self,
2182 unsigned long action, void *data)
2184 struct fb_info *info = (struct fb_info *) data;
2187 case FB_EVENT_SUSPEND:
2188 fbcon_suspended(info);
2190 case FB_EVENT_RESUME:
2191 fbcon_resumed(info);
2198 * The console `switch' structure for the frame buffer based console
2201 const struct consw fb_con = {
2202 .owner = THIS_MODULE,
2203 .con_startup = fbcon_startup,
2204 .con_init = fbcon_init,
2205 .con_deinit = fbcon_deinit,
2206 .con_clear = fbcon_clear,
2207 .con_putc = fbcon_putc,
2208 .con_putcs = fbcon_putcs,
2209 .con_cursor = fbcon_cursor,
2210 .con_scroll = fbcon_scroll,
2211 .con_bmove = fbcon_bmove,
2212 .con_switch = fbcon_switch,
2213 .con_blank = fbcon_blank,
2214 .con_font_op = fbcon_font_op,
2215 .con_set_palette = fbcon_set_palette,
2216 .con_scrolldelta = fbcon_scrolldelta,
2217 .con_set_origin = fbcon_set_origin,
2218 .con_invert_region = fbcon_invert_region,
2219 .con_screen_pos = fbcon_screen_pos,
2220 .con_getxy = fbcon_getxy,
2221 .con_resize = fbcon_resize,
2224 static struct notifier_block fbcon_event_notifer = {
2225 .notifier_call = fbcon_event_notify,
2228 static int fbcon_event_notifier_registered;
2230 int __init fb_console_init(void)
2234 if (!num_registered_fb)
2237 err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
2242 acquire_console_sem();
2243 if (!fbcon_event_notifier_registered) {
2244 fb_register_client(&fbcon_event_notifer);
2245 fbcon_event_notifier_registered = 1;
2247 release_console_sem();
2253 void __exit fb_console_exit(void)
2255 acquire_console_sem();
2256 if (fbcon_event_notifier_registered) {
2257 fb_unregister_client(&fbcon_event_notifer);
2258 fbcon_event_notifier_registered = 0;
2260 release_console_sem();
2261 give_up_console(&fb_con);
2264 module_init(fb_console_init);
2265 module_exit(fb_console_exit);
2270 * Visible symbols for modules
2273 EXPORT_SYMBOL(fb_display);
2274 EXPORT_SYMBOL(fb_con);
2276 MODULE_LICENSE("GPL");