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;
604 int cap = info->flags;
606 if (vc->vc_num != display_fg || (info->flags & FBINFO_MODULE) ||
607 (info->fix.type == FB_TYPE_TEXT))
610 info->var.xoffset = info->var.yoffset = p->yscroll = 0; /* reset wrap/pan */
612 /* If we are not the first console on this
613 fb, copy the font from that console */
614 t = &fb_display[display_fg];
615 vc->vc_font.width = (*default_mode)->vc_font.width;
616 vc->vc_font.height = (*default_mode)->vc_font.height;
617 vc->vc_font.data = p->fontdata = t->fontdata;
618 p->userfont = t->userfont;
620 REFCOUNT(p->fontdata)++;
621 charcnt = FNTCHARCNT(p->fontdata);
623 con_copy_unimap(vc->vc_num, display_fg);
625 vc->vc_can_do_color = info->var.bits_per_pixel != 1;
626 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
627 if (charcnt == 256) {
628 vc->vc_hi_font_mask = 0;
630 vc->vc_hi_font_mask = 0x100;
631 if (vc->vc_can_do_color)
632 vc->vc_complement_mask <<= 1;
635 cols = info->var.xres / vc->vc_font.width;
636 rows = info->var.yres / vc->vc_font.height;
637 vc_resize(vc->vc_num, cols, rows);
639 if ((cap & FBINFO_HWACCEL_COPYAREA) && !(cap & FBINFO_HWACCEL_DISABLED))
640 p->scrollmode = SCROLL_ACCEL;
641 else /* default to something safe */
642 p->scrollmode = SCROLL_REDRAW;
645 * ++guenther: console.c:vc_allocate() relies on initializing
646 * vc_{cols,rows}, but we must not set those if we are only
647 * resizing the console.
655 /* Need to make room for the logo */
659 logo_height = fb_prepare_logo(info);
660 logo_lines = (logo_height + vc->vc_font.height - 1) /
662 q = (unsigned short *) (vc->vc_origin +
663 vc->vc_size_row * rows);
664 step = logo_lines * cols;
665 for (r = q - logo_lines * cols; r < q; r++)
666 if (scr_readw(r) != vc->vc_video_erase_char)
668 if (r != q && rows >= rows + logo_lines) {
669 save = kmalloc(logo_lines * cols * 2, GFP_KERNEL);
671 scr_memsetw(save, vc->vc_video_erase_char,
672 logo_lines * cols * 2);
674 for (cnt = 0; cnt < logo_lines; cnt++, r += cols)
675 scr_memcpyw(save + cnt * cols, r, 2 * cols);
680 /* We can scroll screen down */
682 for (cnt = rows - logo_lines; cnt > 0; cnt--) {
683 scr_memcpyw(r + step, r, vc->vc_size_row);
687 vc->vc_y += logo_lines;
688 vc->vc_pos += logo_lines * vc->vc_size_row;
691 scr_memsetw((unsigned short *) vc->vc_origin,
692 vc->vc_video_erase_char,
693 vc->vc_size_row * logo_lines);
695 if (CON_IS_VISIBLE(vc) && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
696 accel_clear_margins(vc, info, 0);
697 update_screen(vc->vc_num);
700 q = (unsigned short *) (vc->vc_origin +
703 scr_memcpyw(q, save, logo_lines * cols * 2);
704 vc->vc_y += logo_lines;
705 vc->vc_pos += logo_lines * vc->vc_size_row;
708 if (logo_lines > vc->vc_bottom) {
711 "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
714 vc->vc_top = logo_lines;
718 if (vc->vc_num == display_fg && softback_buf) {
719 int l = fbcon_softback_size / vc->vc_size_row;
721 softback_end = softback_buf + l * vc->vc_size_row;
723 /* Smaller scrollback makes no sense, and 0 would screw
724 the operation totally */
730 static void fbcon_deinit(struct vc_data *vc)
732 struct display *p = &fb_display[vc->vc_num];
737 /* ====================================================================== */
739 /* fbcon_XXX routines - interface used by the world
741 * This system is now divided into two levels because of complications
742 * caused by hardware scrolling. Top level functions:
744 * fbcon_bmove(), fbcon_clear(), fbcon_putc()
746 * handles y values in range [0, scr_height-1] that correspond to real
747 * screen positions. y_wrap shift means that first line of bitmap may be
748 * anywhere on this display. These functions convert lineoffsets to
749 * bitmap offsets and deal with the wrap-around case by splitting blits.
751 * fbcon_bmove_physical_8() -- These functions fast implementations
752 * fbcon_clear_physical_8() -- of original fbcon_XXX fns.
753 * fbcon_putc_physical_8() -- (font width != 8) may be added later
757 * At the moment fbcon_putc() cannot blit across vertical wrap boundary
758 * Implies should only really hardware scroll in rows. Only reason for
759 * restriction is simplicity & efficiency at the moment.
762 static __inline__ int real_y(struct display *p, int ypos)
767 return ypos < rows ? ypos : ypos - rows;
771 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
774 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
776 struct display *p = &fb_display[vc->vc_num];
779 if (!info->fbops->fb_blank && console_blanked)
781 if (info->state != FBINFO_STATE_RUNNING)
784 if (!height || !width)
787 /* Split blits that cross physical y_wrap boundary */
789 y_break = p->vrows - p->yscroll;
790 if (sy < y_break && sy + height - 1 >= y_break) {
791 u_int b = y_break - sy;
792 accel_clear(vc, info, real_y(p, sy), sx, b, width);
793 accel_clear(vc, info, real_y(p, sy + b), sx, height - b,
796 accel_clear(vc, info, real_y(p, sy), sx, height, width);
799 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
801 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
802 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
803 unsigned int scan_align = info->pixmap.scan_align - 1;
804 unsigned int buf_align = info->pixmap.buf_align - 1;
805 unsigned int width = (vc->vc_font.width + 7) >> 3;
806 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
807 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
808 struct display *p = &fb_display[vc->vc_num];
809 unsigned int size, pitch;
810 struct fb_image image;
813 if (!info->fbops->fb_blank && console_blanked)
815 if (info->state != FBINFO_STATE_RUNNING)
818 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
821 image.dx = xpos * vc->vc_font.width;
822 image.dy = real_y(p, ypos) * vc->vc_font.height;
823 image.width = vc->vc_font.width;
824 image.height = vc->vc_font.height;
825 image.fg_color = attr_fgcol(fgshift, c);
826 image.bg_color = attr_bgcol(bgshift, c);
829 src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * width;
831 pitch = width + scan_align;
832 pitch &= ~scan_align;
833 size = pitch * vc->vc_font.height;
837 dst = fb_get_buffer_offset(info, &info->pixmap, size);
840 fb_move_buf_aligned(info, &info->pixmap, dst, pitch, src, width, image.height);
842 info->fbops->fb_imageblit(info, &image);
845 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
846 int count, int ypos, int xpos)
848 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
849 struct display *p = &fb_display[vc->vc_num];
851 if (!info->fbops->fb_blank && console_blanked)
853 if (info->state != FBINFO_STATE_RUNNING)
856 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
859 accel_putcs(vc, info, s, count, real_y(p, ypos), xpos);
862 static void fbcon_cursor(struct vc_data *vc, int mode)
864 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
865 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
866 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
867 int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
868 struct display *p = &fb_display[vc->vc_num];
869 int w = (vc->vc_font.width + 7) >> 3, c;
870 int y = real_y(p, vc->vc_y);
871 struct fb_cursor cursor;
873 if (mode & CM_SOFTBACK) {
874 mode &= ~CM_SOFTBACK;
875 if (softback_lines) {
876 if (y + softback_lines >= vc->vc_rows)
881 } else if (softback_lines)
882 fbcon_set_origin(vc);
884 c = scr_readw((u16 *) vc->vc_pos);
886 cursor.image.data = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
887 cursor.set = FB_CUR_SETCUR;
888 cursor.image.depth = 1;
892 if (info->cursor.rop == ROP_XOR) {
893 info->cursor.enable = 0;
894 info->cursor.rop = ROP_COPY;
895 info->fbops->fb_cursor(info, &cursor);
900 info->cursor.enable = 1;
902 if (info->cursor.image.fg_color != attr_fgcol(fgshift, c) ||
903 info->cursor.image.bg_color != attr_bgcol(bgshift, c)) {
904 cursor.image.fg_color = attr_fgcol(fgshift, c);
905 cursor.image.bg_color = attr_bgcol(bgshift, c);
906 cursor.set |= FB_CUR_SETCMAP;
909 if ((info->cursor.image.dx != (vc->vc_font.width * vc->vc_x)) ||
910 (info->cursor.image.dy != (vc->vc_font.height * y))) {
911 cursor.image.dx = vc->vc_font.width * vc->vc_x;
912 cursor.image.dy = vc->vc_font.height * y;
913 cursor.set |= FB_CUR_SETPOS;
916 if (info->cursor.image.height != vc->vc_font.height ||
917 info->cursor.image.width != vc->vc_font.width) {
918 cursor.image.height = vc->vc_font.height;
919 cursor.image.width = vc->vc_font.width;
920 cursor.set |= FB_CUR_SETSIZE;
923 if (info->cursor.hot.x || info->cursor.hot.y) {
924 cursor.hot.x = cursor.hot.y = 0;
925 cursor.set |= FB_CUR_SETHOT;
928 if ((cursor.set & FB_CUR_SETSIZE) || ((vc->vc_cursor_type & 0x0f) != p->cursor_shape)) {
929 char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
930 int cur_height, size, i = 0;
934 if (info->cursor.mask)
935 kfree(info->cursor.mask);
936 info->cursor.mask = mask;
938 p->cursor_shape = vc->vc_cursor_type & 0x0f;
939 cursor.set |= FB_CUR_SETSHAPE;
941 switch (vc->vc_cursor_type & 0x0f) {
946 cur_height = (vc->vc_font.height < 10) ? 1 : 2;
948 case CUR_LOWER_THIRD:
949 cur_height = vc->vc_font.height/3;
952 cur_height = vc->vc_font.height >> 1;
955 cur_height = (vc->vc_font.height << 1)/3;
959 cur_height = vc->vc_font.height;
962 size = (vc->vc_font.height - cur_height) * w;
965 size = cur_height * w;
969 info->cursor.rop = ROP_XOR;
970 info->fbops->fb_cursor(info, &cursor);
971 vbl_cursor_cnt = CURSOR_DRAW_DELAY;
976 static int scrollback_phys_max = 0;
977 static int scrollback_max = 0;
978 static int scrollback_current = 0;
980 int update_var(int con, struct fb_info *info)
982 if (con == info->currcon)
983 return fb_pan_display(info, &info->var);
987 static __inline__ void ywrap_up(struct vc_data *vc, int count)
989 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
990 struct display *p = &fb_display[vc->vc_num];
993 if (p->yscroll >= p->vrows) /* Deal with wrap */
994 p->yscroll -= p->vrows;
995 info->var.xoffset = 0;
996 info->var.yoffset = p->yscroll * vc->vc_font.height;
997 info->var.vmode |= FB_VMODE_YWRAP;
998 update_var(vc->vc_num, info);
999 scrollback_max += count;
1000 if (scrollback_max > scrollback_phys_max)
1001 scrollback_max = scrollback_phys_max;
1002 scrollback_current = 0;
1005 static __inline__ void ywrap_down(struct vc_data *vc, int count)
1007 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1008 struct display *p = &fb_display[vc->vc_num];
1010 p->yscroll -= count;
1011 if (p->yscroll < 0) /* Deal with wrap */
1012 p->yscroll += p->vrows;
1013 info->var.xoffset = 0;
1014 info->var.yoffset = p->yscroll * vc->vc_font.height;
1015 info->var.vmode |= FB_VMODE_YWRAP;
1016 update_var(vc->vc_num, info);
1017 scrollback_max -= count;
1018 if (scrollback_max < 0)
1020 scrollback_current = 0;
1023 static __inline__ void ypan_up(struct vc_data *vc, int count)
1025 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1026 struct display *p = &fb_display[vc->vc_num];
1028 p->yscroll += count;
1029 if (p->yscroll > p->vrows - vc->vc_rows) {
1030 accel_bmove(vc, info, p->vrows - vc->vc_rows,
1031 0, 0, 0, vc->vc_rows, vc->vc_cols);
1032 p->yscroll -= p->vrows - vc->vc_rows;
1034 info->var.xoffset = 0;
1035 info->var.yoffset = p->yscroll * vc->vc_font.height;
1036 info->var.vmode &= ~FB_VMODE_YWRAP;
1037 update_var(vc->vc_num, info);
1038 accel_clear_margins(vc, info, 1);
1039 scrollback_max += count;
1040 if (scrollback_max > scrollback_phys_max)
1041 scrollback_max = scrollback_phys_max;
1042 scrollback_current = 0;
1045 static __inline__ void ypan_down(struct vc_data *vc, int count)
1047 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1048 struct display *p = &fb_display[vc->vc_num];
1050 p->yscroll -= count;
1051 if (p->yscroll < 0) {
1052 accel_bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
1053 0, vc->vc_rows, vc->vc_cols);
1054 p->yscroll += p->vrows - vc->vc_rows;
1056 info->var.xoffset = 0;
1057 info->var.yoffset = p->yscroll * vc->vc_font.height;
1058 info->var.vmode &= ~FB_VMODE_YWRAP;
1059 update_var(vc->vc_num, info);
1060 accel_clear_margins(vc, info, 1);
1061 scrollback_max -= count;
1062 if (scrollback_max < 0)
1064 scrollback_current = 0;
1067 static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
1070 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1071 int count = vc->vc_rows;
1072 unsigned short *d, *s;
1076 d = (u16 *) softback_curr;
1077 if (d == (u16 *) softback_in)
1078 d = (u16 *) vc->vc_origin;
1079 n = softback_curr + delta * vc->vc_size_row;
1080 softback_lines -= delta;
1082 if (softback_curr < softback_top && n < softback_buf) {
1083 n += softback_end - softback_buf;
1084 if (n < softback_top) {
1086 (softback_top - n) / vc->vc_size_row;
1089 } else if (softback_curr >= softback_top
1090 && n < softback_top) {
1092 (softback_top - n) / vc->vc_size_row;
1096 if (softback_curr > softback_in && n >= softback_end) {
1097 n += softback_buf - softback_end;
1098 if (n > softback_in) {
1102 } else if (softback_curr <= softback_in && n > softback_in) {
1107 if (n == softback_curr)
1110 s = (u16 *) softback_curr;
1111 if (s == (u16 *) softback_in)
1112 s = (u16 *) vc->vc_origin;
1114 unsigned short *start;
1118 unsigned short attr = 1;
1121 le = advance_row(s, 1);
1124 if (attr != (c & 0xff00)) {
1127 accel_putcs(vc, info, start, s - start,
1128 real_y(p, line), x);
1133 if (c == scr_readw(d)) {
1135 accel_putcs(vc, info, start, s - start,
1136 real_y(p, line), x);
1148 accel_putcs(vc, info, start, s - start,
1149 real_y(p, line), x);
1151 if (d == (u16 *) softback_end)
1152 d = (u16 *) softback_buf;
1153 if (d == (u16 *) softback_in)
1154 d = (u16 *) vc->vc_origin;
1155 if (s == (u16 *) softback_end)
1156 s = (u16 *) softback_buf;
1157 if (s == (u16 *) softback_in)
1158 s = (u16 *) vc->vc_origin;
1162 static void fbcon_redraw(struct vc_data *vc, struct display *p,
1163 int line, int count, int offset)
1165 unsigned short *d = (unsigned short *)
1166 (vc->vc_origin + vc->vc_size_row * line);
1167 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1168 unsigned short *s = d + offset;
1171 unsigned short *start = s;
1172 unsigned short *le = advance_row(s, 1);
1175 unsigned short attr = 1;
1179 if (attr != (c & 0xff00)) {
1182 accel_putcs(vc, info, start, s - start,
1183 real_y(p, line), x);
1188 if (c == scr_readw(d)) {
1190 accel_putcs(vc, info, start, s - start,
1191 real_y(p, line), x);
1200 console_conditional_schedule();
1205 accel_putcs(vc, info, start, s - start,
1206 real_y(p, line), x);
1207 console_conditional_schedule();
1212 /* NOTE: We subtract two lines from these pointers */
1213 s -= vc->vc_size_row;
1214 d -= vc->vc_size_row;
1219 static inline void fbcon_softback_note(struct vc_data *vc, int t,
1224 if (vc->vc_num != fg_console)
1226 p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
1229 scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row);
1231 p = advance_row(p, 1);
1232 softback_in += vc->vc_size_row;
1233 if (softback_in == softback_end)
1234 softback_in = softback_buf;
1235 if (softback_in == softback_top) {
1236 softback_top += vc->vc_size_row;
1237 if (softback_top == softback_end)
1238 softback_top = softback_buf;
1241 softback_curr = softback_in;
1244 static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
1247 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1248 struct display *p = &fb_display[vc->vc_num];
1249 int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
1251 if (!info->fbops->fb_blank && console_blanked)
1254 if (!count || vt_cons[vc->vc_num]->vc_mode != KD_TEXT)
1257 fbcon_cursor(vc, CM_ERASE);
1260 * ++Geert: Only use ywrap/ypan if the console is in text mode
1261 * ++Andrew: Only use ypan on hardware text mode when scrolling the
1262 * whole screen (prevents flicker).
1267 if (count > vc->vc_rows) /* Maximum realistic size */
1268 count = vc->vc_rows;
1270 fbcon_softback_note(vc, t, count);
1271 if (logo_shown >= 0)
1273 switch (p->scrollmode) {
1275 accel_bmove(vc, info, t + count, 0, t, 0,
1276 b - t - count, vc->vc_cols);
1277 accel_clear(vc, info, b - count, 0, count,
1282 if (b - t - count > 3 * vc->vc_rows >> 2) {
1284 fbcon_bmove(vc, 0, 0, count, 0, t,
1286 ywrap_up(vc, count);
1287 if (vc->vc_rows - b > 0)
1288 fbcon_bmove(vc, b - count, 0, b, 0,
1291 } else if (info->flags & FBINFO_READS_FAST)
1292 fbcon_bmove(vc, t + count, 0, t, 0,
1293 b - t - count, vc->vc_cols);
1296 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1300 if ((p->yscroll + count <=
1301 2 * (p->vrows - vc->vc_rows))
1302 && ((!scroll_partial && (b - t == vc->vc_rows))
1305 3 * vc->vc_rows >> 2)))) {
1307 fbcon_bmove(vc, 0, 0, count, 0, t,
1310 if (vc->vc_rows - b > 0)
1311 fbcon_bmove(vc, b - count, 0, b, 0,
1314 } else if (info->flags & FBINFO_READS_FAST)
1315 fbcon_bmove(vc, t + count, 0, t, 0,
1316 b - t - count, vc->vc_cols);
1319 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1324 fbcon_redraw(vc, p, t, b - t - count,
1325 count * vc->vc_cols);
1326 accel_clear(vc, info, real_y(p, b - count), 0,
1327 count, vc->vc_cols);
1328 scr_memsetw((unsigned short *) (vc->vc_origin +
1331 vc->vc_video_erase_char,
1332 vc->vc_size_row * count);
1338 if (count > vc->vc_rows) /* Maximum realistic size */
1339 count = vc->vc_rows;
1340 switch (p->scrollmode) {
1342 accel_bmove(vc, info, t, 0, t + count, 0,
1343 b - t - count, vc->vc_cols);
1344 accel_clear(vc, info, t, 0, count, vc->vc_cols);
1348 if (b - t - count > 3 * vc->vc_rows >> 2) {
1349 if (vc->vc_rows - b > 0)
1350 fbcon_bmove(vc, b, 0, b - count, 0,
1353 ywrap_down(vc, count);
1355 fbcon_bmove(vc, count, 0, 0, 0, t,
1357 } else if (info->flags & FBINFO_READS_FAST)
1358 fbcon_bmove(vc, t, 0, t + count, 0,
1359 b - t - count, vc->vc_cols);
1362 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1366 if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1367 && ((!scroll_partial && (b - t == vc->vc_rows))
1370 3 * vc->vc_rows >> 2)))) {
1371 if (vc->vc_rows - b > 0)
1372 fbcon_bmove(vc, b, 0, b - count, 0,
1375 ypan_down(vc, count);
1377 fbcon_bmove(vc, count, 0, 0, 0, t,
1379 } else if (info->flags & FBINFO_READS_FAST)
1380 fbcon_bmove(vc, t, 0, t + count, 0,
1381 b - t - count, vc->vc_cols);
1384 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1389 fbcon_redraw(vc, p, b - 1, b - t - count,
1390 -count * vc->vc_cols);
1391 accel_clear(vc, info, real_y(p, t), 0, count,
1393 scr_memsetw((unsigned short *) (vc->vc_origin +
1396 vc->vc_video_erase_char,
1397 vc->vc_size_row * count);
1405 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
1406 int height, int width)
1408 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1409 struct display *p = &fb_display[vc->vc_num];
1411 if (!info->fbops->fb_blank && console_blanked)
1414 if (!width || !height)
1417 /* Split blits that cross physical y_wrap case.
1418 * Pathological case involves 4 blits, better to use recursive
1419 * code rather than unrolled case
1421 * Recursive invocations don't need to erase the cursor over and
1422 * over again, so we use fbcon_bmove_rec()
1424 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
1425 p->vrows - p->yscroll);
1428 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
1429 int dy, int dx, int height, int width, u_int y_break)
1431 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1434 if (sy < y_break && sy + height > y_break) {
1436 if (dy < sy) { /* Avoid trashing self */
1437 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1439 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1440 height - b, width, y_break);
1442 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1443 height - b, width, y_break);
1444 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1450 if (dy < y_break && dy + height > y_break) {
1452 if (dy < sy) { /* Avoid trashing self */
1453 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1455 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1456 height - b, width, y_break);
1458 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1459 height - b, width, y_break);
1460 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1465 accel_bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
1469 static __inline__ void updatescrollmode(struct display *p, struct fb_info *info, struct vc_data *vc)
1471 int cap = info->flags;
1472 int good_pan = (cap & FBINFO_HWACCEL_YPAN)
1473 && divides(info->fix.ypanstep, vc->vc_font.height)
1474 && info->var.yres_virtual >= 2*info->var.yres;
1475 int good_wrap = (cap & FBINFO_HWACCEL_YWRAP)
1476 && divides(info->fix.ywrapstep, vc->vc_font.height)
1477 && divides(vc->vc_font.height, info->var.yres_virtual);
1478 int reading_fast = cap & FBINFO_READS_FAST;
1479 int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) && !(cap & FBINFO_HWACCEL_DISABLED);
1481 if (good_wrap || good_pan) {
1482 if (reading_fast || fast_copyarea)
1483 p->scrollmode = good_wrap ? SCROLL_WRAP : SCROLL_PAN;
1485 p->scrollmode = SCROLL_REDRAW;
1487 if (reading_fast || fast_copyarea)
1488 p->scrollmode = SCROLL_ACCEL;
1490 p->scrollmode = SCROLL_REDRAW;
1494 static int fbcon_resize(struct vc_data *vc, unsigned int width,
1495 unsigned int height)
1497 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1498 struct display *p = &fb_display[vc->vc_num];
1499 struct fb_var_screeninfo var = info->var;
1500 int err; int x_diff, y_diff;
1501 int fw = vc->vc_font.width;
1502 int fh = vc->vc_font.height;
1504 var.xres = width * fw;
1505 var.yres = height * fh;
1506 x_diff = info->var.xres - var.xres;
1507 y_diff = info->var.yres - var.yres;
1508 if (x_diff < 0 || x_diff > fw || (y_diff < 0 || y_diff > fh)) {
1511 DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
1512 if (!info->fbops->fb_set_par)
1515 snprintf(mode, 40, "%ix%i", var.xres, var.yres);
1516 err = fb_find_mode(&var, info, mode, info->monspecs.modedb,
1517 info->monspecs.modedb_len, NULL,
1518 info->var.bits_per_pixel);
1519 if (!err || width > var.xres/fw || height > var.yres/fh)
1521 DPRINTK("resize now %ix%i\n", var.xres, var.yres);
1522 if (CON_IS_VISIBLE(vc)) {
1523 var.activate = FB_ACTIVATE_NOW;
1524 fb_set_var(info, &var);
1527 p->vrows = var.yres_virtual/fh;
1528 if (var.yres > (fh * (height + 1)))
1529 p->vrows -= (var.yres - (fh * height)) / fh;
1530 if ((var.yres % fh) && (var.yres_virtual % fh < var.yres % fh))
1532 updatescrollmode(p, info, vc);
1536 static int fbcon_switch(struct vc_data *vc)
1538 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1539 struct display *p = &fb_display[vc->vc_num];
1542 int l = fbcon_softback_size / vc->vc_size_row;
1544 fbcon_set_origin(vc);
1545 softback_top = softback_curr = softback_in = softback_buf;
1549 softback_end = softback_buf + l * vc->vc_size_row;
1551 /* Smaller scrollback makes no sense, and 0 would screw
1552 the operation totally */
1556 if (logo_shown >= 0) {
1557 struct vc_data *conp2 = vc_cons[logo_shown].d;
1559 if (conp2->vc_top == logo_lines
1560 && conp2->vc_bottom == conp2->vc_rows)
1565 info->var.yoffset = p->yscroll = 0;
1566 fbcon_resize(vc, vc->vc_cols, vc->vc_rows);
1567 switch (p->scrollmode) {
1569 scrollback_phys_max = p->vrows - vc->vc_rows;
1572 scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
1573 if (scrollback_phys_max < 0)
1574 scrollback_phys_max = 0;
1577 scrollback_phys_max = 0;
1581 scrollback_current = 0;
1583 info->currcon = vc->vc_num;
1585 update_var(vc->vc_num, info);
1586 fbcon_set_palette(vc, color_table);
1588 if (vt_cons[vc->vc_num]->vc_mode == KD_TEXT)
1589 accel_clear_margins(vc, info, 0);
1590 if (logo_shown == -2) {
1591 logo_shown = fg_console;
1592 /* This is protected above by initmem_freed */
1594 update_region(fg_console,
1595 vc->vc_origin + vc->vc_size_row * vc->vc_top,
1596 vc->vc_size_row * (vc->vc_bottom -
1603 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
1605 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
1606 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1607 struct display *p = &fb_display[vc->vc_num];
1610 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1611 struct fb_var_screeninfo var = info->var;
1614 fbcon_cursor(vc, CM_ERASE);
1617 var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
1618 fb_set_var(info, &var);
1621 fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
1623 if (!info->fbops->fb_blank) {
1625 unsigned short oldc;
1629 oldc = vc->vc_video_erase_char;
1630 vc->vc_video_erase_char &= charmask;
1631 height = vc->vc_rows;
1632 y_break = p->vrows - p->yscroll;
1633 if (height > y_break) {
1634 accel_clear(vc, info, real_y(p, 0),
1635 0, y_break, vc->vc_cols);
1636 accel_clear(vc, info, real_y(p, y_break),
1637 0, height - y_break,
1640 accel_clear(vc, info, real_y(p, 0),
1641 0, height, vc->vc_cols);
1642 vc->vc_video_erase_char = oldc;
1644 update_screen(vc->vc_num);
1647 return fb_blank(info, blank);
1650 static void fbcon_free_font(struct display *p)
1652 if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
1653 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
1658 static inline int fbcon_get_font(struct vc_data *vc, struct console_font_op *op)
1660 u8 *fontdata = vc->vc_font.data;
1661 u8 *data = op->data;
1664 op->width = vc->vc_font.width;
1665 op->height = vc->vc_font.height;
1666 op->charcount = vc->vc_hi_font_mask ? 512 : 256;
1670 if (op->width <= 8) {
1671 j = vc->vc_font.height;
1672 for (i = 0; i < op->charcount; i++) {
1673 memcpy(data, fontdata, j);
1674 memset(data + j, 0, 32 - j);
1678 } else if (op->width <= 16) {
1679 j = vc->vc_font.height * 2;
1680 for (i = 0; i < op->charcount; i++) {
1681 memcpy(data, fontdata, j);
1682 memset(data + j, 0, 64 - j);
1686 } else if (op->width <= 24) {
1687 for (i = 0; i < op->charcount; i++) {
1688 for (j = 0; j < vc->vc_font.height; j++) {
1689 *data++ = fontdata[0];
1690 *data++ = fontdata[1];
1691 *data++ = fontdata[2];
1692 fontdata += sizeof(u32);
1694 memset(data, 0, 3 * (32 - j));
1695 data += 3 * (32 - j);
1698 j = vc->vc_font.height * 4;
1699 for (i = 0; i < op->charcount; i++) {
1700 memcpy(data, fontdata, j);
1701 memset(data + j, 0, 128 - j);
1709 static int fbcon_do_set_font(struct vc_data *vc, struct console_font_op *op,
1710 u8 * data, int userfont)
1712 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1713 struct display *p = &fb_display[vc->vc_num];
1718 char *old_data = NULL;
1721 if (userfont && op->op != KD_FONT_OP_COPY)
1722 kfree(data - FONT_EXTRA_WORDS * sizeof(int));
1726 if (CON_IS_VISIBLE(vc) && softback_lines)
1727 fbcon_set_origin(vc);
1729 resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
1731 old_data = vc->vc_font.data;
1733 cnt = FNTCHARCNT(data);
1736 vc->vc_font.data = p->fontdata = data;
1737 if ((p->userfont = userfont))
1739 vc->vc_font.width = w;
1740 vc->vc_font.height = h;
1741 if (vc->vc_hi_font_mask && cnt == 256) {
1742 vc->vc_hi_font_mask = 0;
1743 if (vc->vc_can_do_color) {
1744 vc->vc_complement_mask >>= 1;
1745 vc->vc_s_complement_mask >>= 1;
1748 /* ++Edmund: reorder the attribute bits */
1749 if (vc->vc_can_do_color) {
1750 unsigned short *cp =
1751 (unsigned short *) vc->vc_origin;
1752 int count = vc->vc_screenbuf_size / 2;
1754 for (; count > 0; count--, cp++) {
1756 scr_writew(((c & 0xfe00) >> 1) |
1759 c = vc->vc_video_erase_char;
1760 vc->vc_video_erase_char =
1761 ((c & 0xfe00) >> 1) | (c & 0xff);
1764 } else if (!vc->vc_hi_font_mask && cnt == 512) {
1765 vc->vc_hi_font_mask = 0x100;
1766 if (vc->vc_can_do_color) {
1767 vc->vc_complement_mask <<= 1;
1768 vc->vc_s_complement_mask <<= 1;
1771 /* ++Edmund: reorder the attribute bits */
1773 unsigned short *cp =
1774 (unsigned short *) vc->vc_origin;
1775 int count = vc->vc_screenbuf_size / 2;
1777 for (; count > 0; count--, cp++) {
1778 unsigned short newc;
1780 if (vc->vc_can_do_color)
1782 ((c & 0xff00) << 1) | (c &
1786 scr_writew(newc, cp);
1788 c = vc->vc_video_erase_char;
1789 if (vc->vc_can_do_color) {
1790 vc->vc_video_erase_char =
1791 ((c & 0xff00) << 1) | (c & 0xff);
1794 vc->vc_video_erase_char = c & ~0x100;
1800 /* reset wrap/pan */
1801 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
1802 vc_resize(vc->vc_num, info->var.xres / w, info->var.yres / h);
1803 if (CON_IS_VISIBLE(vc) && softback_buf) {
1804 int l = fbcon_softback_size / vc->vc_size_row;
1807 softback_buf + l * vc->vc_size_row;
1809 /* Smaller scrollback makes no sense, and 0 would screw
1810 the operation totally */
1814 } else if (CON_IS_VISIBLE(vc)
1815 && vt_cons[vc->vc_num]->vc_mode == KD_TEXT) {
1816 accel_clear_margins(vc, info, 0);
1817 update_screen(vc->vc_num);
1820 if (old_data && (--REFCOUNT(old_data) == 0))
1821 kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
1825 static inline int fbcon_copy_font(struct vc_data *vc, struct console_font_op *op)
1830 if (h < 0 || !vc_cons_allocated(h))
1832 if (h == vc->vc_num)
1833 return 0; /* nothing to do */
1834 od = &fb_display[h];
1835 if (od->fontdata == vc->vc_font.data)
1836 return 0; /* already the same font... */
1837 op->width = vc->vc_font.width;
1838 op->height = vc->vc_font.height;
1839 return fbcon_do_set_font(vc, op, od->fontdata, od->userfont);
1842 static inline int fbcon_set_font(struct vc_data *vc, struct console_font_op *op)
1848 u8 *new_data, *data = op->data, *p;
1850 if ((w <= 0) || (w > 32)
1851 || (op->charcount != 256 && op->charcount != 512))
1860 size *= op->charcount;
1864 kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER)))
1866 new_data += FONT_EXTRA_WORDS * sizeof(int);
1867 FNTSIZE(new_data) = size;
1868 FNTCHARCNT(new_data) = op->charcount;
1869 REFCOUNT(new_data) = 0; /* usage counter */
1872 for (i = 0; i < op->charcount; i++) {
1877 } else if (w <= 16) {
1879 for (i = 0; i < op->charcount; i++) {
1884 } else if (w <= 24) {
1885 for (i = 0; i < op->charcount; i++) {
1887 for (j = 0; j < h; j++) {
1893 data += 3 * (32 - h);
1897 for (i = 0; i < op->charcount; i++) {
1903 /* we can do it in u32 chunks because of charcount is 256 or 512, so
1904 font length must be multiple of 256, at least. And 256 is multiple
1907 while (p > new_data) {
1908 p = (u8 *)((u32 *)p - 1);
1911 FNTSUM(new_data) = k;
1912 /* Check if the same font is on some other console already */
1913 for (i = 0; i < MAX_NR_CONSOLES; i++) {
1914 struct vc_data *tmp = vc_cons[i].d;
1916 if (fb_display[i].userfont &&
1917 fb_display[i].fontdata &&
1918 FNTSUM(fb_display[i].fontdata) == k &&
1919 FNTSIZE(fb_display[i].fontdata) == size &&
1920 tmp->vc_font.width == w &&
1921 !memcmp(fb_display[i].fontdata, new_data, size)) {
1922 kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
1923 new_data = fb_display[i].fontdata;
1927 return fbcon_do_set_font(vc, op, new_data, 1);
1930 static inline int fbcon_set_def_font(struct vc_data *vc, struct console_font_op *op)
1932 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1933 char name[MAX_FONT_NAME];
1934 struct font_desc *f;
1937 f = get_default_font(info->var.xres, info->var.yres);
1938 else if (strncpy_from_user(name, op->data, MAX_FONT_NAME - 1) < 0)
1941 name[MAX_FONT_NAME - 1] = 0;
1942 if (!(f = find_font(name)))
1945 op->width = f->width;
1946 op->height = f->height;
1947 return fbcon_do_set_font(vc, op, f->data, 0);
1950 static int fbcon_font_op(struct vc_data *vc, struct console_font_op *op)
1953 case KD_FONT_OP_SET:
1954 return fbcon_set_font(vc, op);
1955 case KD_FONT_OP_GET:
1956 return fbcon_get_font(vc, op);
1957 case KD_FONT_OP_SET_DEFAULT:
1958 return fbcon_set_def_font(vc, op);
1959 case KD_FONT_OP_COPY:
1960 return fbcon_copy_font(vc, op);
1966 static u16 palette_red[16];
1967 static u16 palette_green[16];
1968 static u16 palette_blue[16];
1970 static struct fb_cmap palette_cmap = {
1971 0, 16, palette_red, palette_green, palette_blue, NULL
1974 static int fbcon_set_palette(struct vc_data *vc, unsigned char *table)
1976 struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]];
1980 if (!vc->vc_can_do_color
1981 || (!info->fbops->fb_blank && console_blanked))
1983 for (i = j = 0; i < 16; i++) {
1985 val = vc->vc_palette[j++];
1986 palette_red[k] = (val << 8) | val;
1987 val = vc->vc_palette[j++];
1988 palette_green[k] = (val << 8) | val;
1989 val = vc->vc_palette[j++];
1990 palette_blue[k] = (val << 8) | val;
1992 if (info->var.bits_per_pixel <= 4)
1993 palette_cmap.len = 1 << info->var.bits_per_pixel;
1995 palette_cmap.len = 16;
1996 palette_cmap.start = 0;
1997 return fb_set_cmap(&palette_cmap, 1, info);
2000 static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
2005 if (vc->vc_num != fg_console || !softback_lines)
2006 return (u16 *) (vc->vc_origin + offset);
2007 line = offset / vc->vc_size_row;
2008 if (line >= softback_lines)
2009 return (u16 *) (vc->vc_origin + offset -
2010 softback_lines * vc->vc_size_row);
2011 p = softback_curr + offset;
2012 if (p >= softback_end)
2013 p += softback_buf - softback_end;
2017 static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
2023 if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
2024 unsigned long offset = (pos - vc->vc_origin) / 2;
2026 x = offset % vc->vc_cols;
2027 y = offset / vc->vc_cols;
2028 if (vc->vc_num == fg_console)
2029 y += softback_lines;
2030 ret = pos + (vc->vc_cols - x) * 2;
2031 } else if (vc->vc_num == fg_console && softback_lines) {
2032 unsigned long offset = pos - softback_curr;
2034 if (pos < softback_curr)
2035 offset += softback_end - softback_buf;
2037 x = offset % vc->vc_cols;
2038 y = offset / vc->vc_cols;
2039 ret = pos + (vc->vc_cols - x) * 2;
2040 if (ret == softback_end)
2042 if (ret == softback_in)
2043 ret = vc->vc_origin;
2045 /* Should not happen */
2047 ret = vc->vc_origin;
2056 /* As we might be inside of softback, we may work with non-contiguous buffer,
2057 that's why we have to use a separate routine. */
2058 static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
2061 u16 a = scr_readw(p);
2062 if (!vc->vc_can_do_color)
2064 else if (vc->vc_hi_font_mask == 0x100)
2065 a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
2066 (((a) & 0x0e00) << 4);
2068 a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
2069 (((a) & 0x0700) << 4);
2071 if (p == (u16 *) softback_end)
2072 p = (u16 *) softback_buf;
2073 if (p == (u16 *) softback_in)
2074 p = (u16 *) vc->vc_origin;
2078 static int fbcon_scrolldelta(struct vc_data *vc, int lines)
2080 struct fb_info *info = registered_fb[(int) con2fb_map[fg_console]];
2081 struct display *p = &fb_display[fg_console];
2082 int offset, limit, scrollback_old;
2085 if (vc->vc_num != fg_console)
2087 if (vt_cons[vc->vc_num]->vc_mode != KD_TEXT || !lines)
2089 if (logo_shown >= 0) {
2090 struct vc_data *conp2 = vc_cons[logo_shown].d;
2092 if (conp2->vc_top == logo_lines
2093 && conp2->vc_bottom == conp2->vc_rows)
2095 if (logo_shown == vc->vc_num) {
2101 logo_lines * vc->vc_size_row;
2102 for (i = 0; i < logo_lines; i++) {
2103 if (p == softback_top)
2105 if (p == softback_buf)
2107 p -= vc->vc_size_row;
2108 q -= vc->vc_size_row;
2109 scr_memcpyw((u16 *) q, (u16 *) p,
2113 update_region(vc->vc_num, vc->vc_origin,
2114 logo_lines * vc->vc_cols);
2118 fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK);
2119 fbcon_redraw_softback(vc, p, lines);
2120 fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK);
2124 if (!scrollback_phys_max)
2127 scrollback_old = scrollback_current;
2128 scrollback_current -= lines;
2129 if (scrollback_current < 0)
2130 scrollback_current = 0;
2131 else if (scrollback_current > scrollback_max)
2132 scrollback_current = scrollback_max;
2133 if (scrollback_current == scrollback_old)
2136 if (!info->fbops->fb_blank &&
2137 (console_blanked || vt_cons[vc->vc_num]->vc_mode != KD_TEXT
2140 fbcon_cursor(vc, CM_ERASE);
2142 offset = p->yscroll - scrollback_current;
2144 switch (p->scrollmode) {
2146 info->var.vmode |= FB_VMODE_YWRAP;
2149 limit -= vc->vc_rows;
2150 info->var.vmode &= ~FB_VMODE_YWRAP;
2155 else if (offset >= limit)
2157 info->var.xoffset = 0;
2158 info->var.yoffset = offset * vc->vc_font.height;
2159 update_var(vc->vc_num, info);
2160 if (!scrollback_current)
2161 fbcon_cursor(vc, CM_DRAW);
2165 static int fbcon_set_origin(struct vc_data *vc)
2167 if (softback_lines && !console_blanked)
2168 fbcon_scrolldelta(vc, softback_lines);
2172 static void fbcon_suspended(struct fb_info *info)
2174 /* Clear cursor, restore saved data */
2175 info->cursor.enable = 0;
2176 info->fbops->fb_cursor(info, &info->cursor);
2179 static void fbcon_resumed(struct fb_info *info)
2183 if (info->currcon < 0)
2185 vc = vc_cons[info->currcon].d;
2187 update_screen(vc->vc_num);
2189 static int fbcon_event_notify(struct notifier_block *self,
2190 unsigned long action, void *data)
2192 struct fb_info *info = (struct fb_info *) data;
2195 case FB_EVENT_SUSPEND:
2196 fbcon_suspended(info);
2198 case FB_EVENT_RESUME:
2199 fbcon_resumed(info);
2206 * The console `switch' structure for the frame buffer based console
2209 const struct consw fb_con = {
2210 .owner = THIS_MODULE,
2211 .con_startup = fbcon_startup,
2212 .con_init = fbcon_init,
2213 .con_deinit = fbcon_deinit,
2214 .con_clear = fbcon_clear,
2215 .con_putc = fbcon_putc,
2216 .con_putcs = fbcon_putcs,
2217 .con_cursor = fbcon_cursor,
2218 .con_scroll = fbcon_scroll,
2219 .con_bmove = fbcon_bmove,
2220 .con_switch = fbcon_switch,
2221 .con_blank = fbcon_blank,
2222 .con_font_op = fbcon_font_op,
2223 .con_set_palette = fbcon_set_palette,
2224 .con_scrolldelta = fbcon_scrolldelta,
2225 .con_set_origin = fbcon_set_origin,
2226 .con_invert_region = fbcon_invert_region,
2227 .con_screen_pos = fbcon_screen_pos,
2228 .con_getxy = fbcon_getxy,
2229 .con_resize = fbcon_resize,
2232 static struct notifier_block fbcon_event_notifer = {
2233 .notifier_call = fbcon_event_notify,
2236 static int fbcon_event_notifier_registered;
2238 int __init fb_console_init(void)
2242 if (!num_registered_fb)
2245 err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
2250 acquire_console_sem();
2251 if (!fbcon_event_notifier_registered) {
2252 fb_register_client(&fbcon_event_notifer);
2253 fbcon_event_notifier_registered = 1;
2255 release_console_sem();
2261 void __exit fb_console_exit(void)
2263 acquire_console_sem();
2264 if (fbcon_event_notifier_registered) {
2265 fb_unregister_client(&fbcon_event_notifer);
2266 fbcon_event_notifier_registered = 0;
2268 release_console_sem();
2269 give_up_console(&fb_con);
2272 module_init(fb_console_init);
2273 module_exit(fb_console_exit);
2278 * Visible symbols for modules
2281 EXPORT_SYMBOL(fb_display);
2282 EXPORT_SYMBOL(fb_con);
2284 MODULE_LICENSE("GPL");