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>
80 #include <linux/crc32.h> /* For counting font checksums */
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...)
104 FBCON_LOGO_CANSHOW
= -1, /* the logo can be shown */
105 FBCON_LOGO_DRAW
= -2, /* draw the logo to a console */
106 FBCON_LOGO_DONTSHOW
= -3 /* do not show the logo */
109 struct display fb_display
[MAX_NR_CONSOLES
];
110 static signed char con2fb_map
[MAX_NR_CONSOLES
];
111 static signed char con2fb_map_boot
[MAX_NR_CONSOLES
];
112 static int logo_height
;
113 static int logo_lines
;
114 /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO
116 static int logo_shown
= FBCON_LOGO_CANSHOW
;
117 /* Software scrollback */
118 static int fbcon_softback_size
= 32768;
119 static unsigned long softback_buf
, softback_curr
;
120 static unsigned long softback_in
;
121 static unsigned long softback_top
, softback_end
;
122 static int softback_lines
;
123 /* console mappings */
124 static int first_fb_vc
;
125 static int last_fb_vc
= MAX_NR_CONSOLES
- 1;
126 static int fbcon_is_default
= 1;
128 static char fontname
[40];
130 /* current fb_info */
131 static int info_idx
= -1;
133 static const struct consw fb_con
;
135 #define CM_SOFTBACK (8)
137 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
139 static void fbcon_free_font(struct display
*);
140 static int fbcon_set_origin(struct vc_data
*);
142 #define CURSOR_DRAW_DELAY (1)
144 /* # VBL ints between cursor state changes */
145 #define ARM_CURSOR_BLINK_RATE (10)
146 #define ATARI_CURSOR_BLINK_RATE (42)
147 #define MAC_CURSOR_BLINK_RATE (32)
148 #define DEFAULT_CURSOR_BLINK_RATE (20)
150 static int vbl_cursor_cnt
;
152 #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1)
155 * Interface used by the world
158 static const char *fbcon_startup(void);
159 static void fbcon_init(struct vc_data
*vc
, int init
);
160 static void fbcon_deinit(struct vc_data
*vc
);
161 static void fbcon_clear(struct vc_data
*vc
, int sy
, int sx
, int height
,
163 static void fbcon_putc(struct vc_data
*vc
, int c
, int ypos
, int xpos
);
164 static void fbcon_putcs(struct vc_data
*vc
, const unsigned short *s
,
165 int count
, int ypos
, int xpos
);
166 static void fbcon_clear_margins(struct vc_data
*vc
, int bottom_only
);
167 static void fbcon_cursor(struct vc_data
*vc
, int mode
);
168 static int fbcon_scroll(struct vc_data
*vc
, int t
, int b
, int dir
,
170 static void fbcon_bmove(struct vc_data
*vc
, int sy
, int sx
, int dy
, int dx
,
171 int height
, int width
);
172 static int fbcon_switch(struct vc_data
*vc
);
173 static int fbcon_blank(struct vc_data
*vc
, int blank
, int mode_switch
);
174 static int fbcon_set_palette(struct vc_data
*vc
, unsigned char *table
);
175 static int fbcon_scrolldelta(struct vc_data
*vc
, int lines
);
180 static __inline__
int real_y(struct display
*p
, int ypos
);
181 static __inline__
void ywrap_up(struct vc_data
*vc
, int count
);
182 static __inline__
void ywrap_down(struct vc_data
*vc
, int count
);
183 static __inline__
void ypan_up(struct vc_data
*vc
, int count
);
184 static __inline__
void ypan_down(struct vc_data
*vc
, int count
);
185 static void fbcon_bmove_rec(struct vc_data
*vc
, struct display
*p
, int sy
, int sx
,
186 int dy
, int dx
, int height
, int width
, u_int y_break
);
187 static void fbcon_set_disp(struct fb_info
*info
, struct fb_var_screeninfo
*var
,
189 static void fbcon_preset_disp(struct fb_info
*info
, struct fb_var_screeninfo
*var
,
191 static void fbcon_redraw_move(struct vc_data
*vc
, struct display
*p
,
192 int line
, int count
, int dy
);
196 * On the Macintoy, there may or may not be a working VBL int. We need to probe
198 static int vbl_detected
;
200 static irqreturn_t
fb_vbl_detect(int irq
, void *dummy
, struct pt_regs
*fp
)
207 static inline int fbcon_is_inactive(struct vc_data
*vc
, struct fb_info
*info
)
209 struct fbcon_ops
*ops
= info
->fbcon_par
;
211 return (info
->state
!= FBINFO_STATE_RUNNING
||
212 vc
->vc_mode
!= KD_TEXT
|| ops
->graphics
);
215 static inline int get_color(struct vc_data
*vc
, struct fb_info
*info
,
218 int depth
= fb_get_color_depth(&info
->var
);
221 if (console_blanked
) {
222 unsigned short charmask
= vc
->vc_hi_font_mask
? 0x1ff : 0xff;
224 c
= vc
->vc_video_erase_char
& charmask
;
228 color
= (is_fg
) ? attr_fgcol((vc
->vc_hi_font_mask
) ? 9 : 8, c
)
229 : attr_bgcol((vc
->vc_hi_font_mask
) ? 13 : 12, c
);
235 int fg
= (info
->fix
.visual
!= FB_VISUAL_MONO01
) ? 1 : 0;
236 int bg
= (info
->fix
.visual
!= FB_VISUAL_MONO01
) ? 0 : 1;
241 color
= (is_fg
) ? fg
: bg
;
246 * Scale down 16-colors to 4 colors. Default 4-color palette
253 * Last 8 entries of default 16-color palette is a more intense
254 * version of the first 8 (i.e., same chrominance, different
265 static void fb_flashcursor(void *private)
267 struct fb_info
*info
= private;
268 struct fbcon_ops
*ops
= info
->fbcon_par
;
270 struct vc_data
*vc
= NULL
;
274 if (ops
->currcon
!= -1)
275 vc
= vc_cons
[ops
->currcon
].d
;
277 if (!vc
|| !CON_IS_VISIBLE(vc
) ||
278 fbcon_is_inactive(vc
, info
) ||
279 registered_fb
[con2fb_map
[vc
->vc_num
]] != info
)
281 acquire_console_sem();
282 p
= &fb_display
[vc
->vc_num
];
283 c
= scr_readw((u16
*) vc
->vc_pos
);
284 mode
= (!ops
->cursor_flash
|| ops
->cursor_state
.enable
) ?
286 ops
->cursor(vc
, info
, p
, mode
, softback_lines
, get_color(vc
, info
, c
, 1),
287 get_color(vc
, info
, c
, 0));
288 release_console_sem();
291 #if (defined(__arm__) && defined(IRQ_VSYNCPULSE)) || defined(CONFIG_ATARI) || defined(CONFIG_MAC)
292 static int cursor_blink_rate
;
293 static irqreturn_t
fb_vbl_handler(int irq
, void *dev_id
, struct pt_regs
*fp
)
295 struct fb_info
*info
= dev_id
;
297 if (vbl_cursor_cnt
&& --vbl_cursor_cnt
== 0) {
298 schedule_work(&info
->queue
);
299 vbl_cursor_cnt
= cursor_blink_rate
;
305 static void cursor_timer_handler(unsigned long dev_addr
)
307 struct fb_info
*info
= (struct fb_info
*) dev_addr
;
308 struct fbcon_ops
*ops
= info
->fbcon_par
;
310 schedule_work(&info
->queue
);
311 mod_timer(&ops
->cursor_timer
, jiffies
+ HZ
/5);
315 static int __init
fb_console_setup(char *this_opt
)
320 if (!this_opt
|| !*this_opt
)
323 while ((options
= strsep(&this_opt
, ",")) != NULL
) {
324 if (!strncmp(options
, "font:", 5))
325 strcpy(fontname
, options
+ 5);
327 if (!strncmp(options
, "scrollback:", 11)) {
330 fbcon_softback_size
= simple_strtoul(options
, &options
, 0);
331 if (*options
== 'k' || *options
== 'K') {
332 fbcon_softback_size
*= 1024;
342 if (!strncmp(options
, "map:", 4)) {
345 for (i
= 0, j
= 0; i
< MAX_NR_CONSOLES
; i
++) {
349 (options
[j
++]-'0') % FB_MAX
;
354 if (!strncmp(options
, "vc:", 3)) {
357 first_fb_vc
= simple_strtoul(options
, &options
, 10) - 1;
360 if (*options
++ == '-')
361 last_fb_vc
= simple_strtoul(options
, &options
, 10) - 1;
362 fbcon_is_default
= 0;
368 __setup("fbcon=", fb_console_setup
);
371 static int search_fb_in_map(int idx
)
375 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++) {
376 if (con2fb_map
[i
] == idx
)
382 static int search_for_mapped_con(void)
386 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++) {
387 if (con2fb_map
[i
] != -1)
393 static int fbcon_takeover(int show_logo
)
397 if (!num_registered_fb
)
401 logo_shown
= FBCON_LOGO_DONTSHOW
;
403 for (i
= first_fb_vc
; i
<= last_fb_vc
; i
++)
404 con2fb_map
[i
] = info_idx
;
406 err
= take_over_console(&fb_con
, first_fb_vc
, last_fb_vc
,
409 for (i
= first_fb_vc
; i
<= last_fb_vc
; i
++) {
418 static void fbcon_prepare_logo(struct vc_data
*vc
, struct fb_info
*info
,
419 int cols
, int rows
, int new_cols
, int new_rows
)
421 /* Need to make room for the logo */
422 int cnt
, erase
= vc
->vc_video_erase_char
, step
;
423 unsigned short *save
= NULL
, *r
, *q
;
426 * remove underline attribute from erase character
427 * if black and white framebuffer.
429 if (fb_get_color_depth(&info
->var
) == 1)
431 logo_height
= fb_prepare_logo(info
);
432 logo_lines
= (logo_height
+ vc
->vc_font
.height
- 1) /
434 q
= (unsigned short *) (vc
->vc_origin
+
435 vc
->vc_size_row
* rows
);
436 step
= logo_lines
* cols
;
437 for (r
= q
- logo_lines
* cols
; r
< q
; r
++)
438 if (scr_readw(r
) != vc
->vc_video_erase_char
)
440 if (r
!= q
&& new_rows
>= rows
+ logo_lines
) {
441 save
= kmalloc(logo_lines
* new_cols
* 2, GFP_KERNEL
);
443 int i
= cols
< new_cols
? cols
: new_cols
;
444 scr_memsetw(save
, erase
, logo_lines
* new_cols
* 2);
446 for (cnt
= 0; cnt
< logo_lines
; cnt
++, r
+= i
)
447 scr_memcpyw(save
+ cnt
* new_cols
, r
, 2 * i
);
452 /* We can scroll screen down */
454 for (cnt
= rows
- logo_lines
; cnt
> 0; cnt
--) {
455 scr_memcpyw(r
+ step
, r
, vc
->vc_size_row
);
459 vc
->vc_y
+= logo_lines
;
460 vc
->vc_pos
+= logo_lines
* vc
->vc_size_row
;
463 scr_memsetw((unsigned short *) vc
->vc_origin
,
465 vc
->vc_size_row
* logo_lines
);
467 if (CON_IS_VISIBLE(vc
) && vc
->vc_mode
== KD_TEXT
) {
468 fbcon_clear_margins(vc
, 0);
473 q
= (unsigned short *) (vc
->vc_origin
+
476 scr_memcpyw(q
, save
, logo_lines
* new_cols
* 2);
477 vc
->vc_y
+= logo_lines
;
478 vc
->vc_pos
+= logo_lines
* vc
->vc_size_row
;
482 if (logo_lines
> vc
->vc_bottom
) {
483 logo_shown
= FBCON_LOGO_CANSHOW
;
485 "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
486 } else if (logo_shown
!= FBCON_LOGO_DONTSHOW
) {
487 logo_shown
= FBCON_LOGO_DRAW
;
488 vc
->vc_top
= logo_lines
;
492 #ifdef CONFIG_FB_TILEBLITTING
493 static void set_blitting_type(struct vc_data
*vc
, struct fb_info
*info
,
496 struct fbcon_ops
*ops
= info
->fbcon_par
;
498 if ((info
->flags
& FBINFO_MISC_TILEBLITTING
))
499 fbcon_set_tileops(vc
, info
, p
, ops
);
501 fbcon_set_bitops(ops
);
504 static void set_blitting_type(struct vc_data
*vc
, struct fb_info
*info
,
507 struct fbcon_ops
*ops
= info
->fbcon_par
;
509 info
->flags
&= ~FBINFO_MISC_TILEBLITTING
;
510 fbcon_set_bitops(ops
);
512 #endif /* CONFIG_MISC_TILEBLITTING */
515 static int con2fb_acquire_newinfo(struct vc_data
*vc
, struct fb_info
*info
,
516 int unit
, int oldidx
)
518 struct fbcon_ops
*ops
= NULL
;
521 if (!try_module_get(info
->fbops
->owner
))
524 if (!err
&& info
->fbops
->fb_open
&&
525 info
->fbops
->fb_open(info
, 0))
529 ops
= kmalloc(sizeof(struct fbcon_ops
), GFP_KERNEL
);
535 memset(ops
, 0, sizeof(struct fbcon_ops
));
536 info
->fbcon_par
= ops
;
537 set_blitting_type(vc
, info
, NULL
);
541 con2fb_map
[unit
] = oldidx
;
542 module_put(info
->fbops
->owner
);
548 static int con2fb_release_oldinfo(struct vc_data
*vc
, struct fb_info
*oldinfo
,
549 struct fb_info
*newinfo
, int unit
,
550 int oldidx
, int found
)
552 struct fbcon_ops
*ops
= oldinfo
->fbcon_par
;
555 if (oldinfo
->fbops
->fb_release
&&
556 oldinfo
->fbops
->fb_release(oldinfo
, 0)) {
557 con2fb_map
[unit
] = oldidx
;
558 if (!found
&& newinfo
->fbops
->fb_release
)
559 newinfo
->fbops
->fb_release(newinfo
, 0);
561 module_put(newinfo
->fbops
->owner
);
566 if (oldinfo
->queue
.func
== fb_flashcursor
)
567 del_timer_sync(&ops
->cursor_timer
);
569 kfree(ops
->cursor_state
.mask
);
570 kfree(ops
->cursor_data
);
571 kfree(oldinfo
->fbcon_par
);
572 oldinfo
->fbcon_par
= NULL
;
573 module_put(oldinfo
->fbops
->owner
);
579 static void con2fb_init_newinfo(struct fb_info
*info
)
581 if (!info
->queue
.func
|| info
->queue
.func
== fb_flashcursor
) {
582 struct fbcon_ops
*ops
= info
->fbcon_par
;
584 if (!info
->queue
.func
)
585 INIT_WORK(&info
->queue
, fb_flashcursor
, info
);
587 init_timer(&ops
->cursor_timer
);
588 ops
->cursor_timer
.function
= cursor_timer_handler
;
589 ops
->cursor_timer
.expires
= jiffies
+ HZ
/ 5;
590 ops
->cursor_timer
.data
= (unsigned long ) info
;
591 add_timer(&ops
->cursor_timer
);
595 static void con2fb_init_display(struct vc_data
*vc
, struct fb_info
*info
,
596 int unit
, int show_logo
)
598 struct fbcon_ops
*ops
= info
->fbcon_par
;
600 ops
->currcon
= fg_console
;
602 if (info
->fbops
->fb_set_par
&& !(ops
->flags
& FBCON_FLAGS_INIT
))
603 info
->fbops
->fb_set_par(info
);
605 ops
->flags
|= FBCON_FLAGS_INIT
;
609 fbcon_set_disp(info
, &info
->var
, vc
);
611 fbcon_preset_disp(info
, &info
->var
, unit
);
614 struct vc_data
*fg_vc
= vc_cons
[fg_console
].d
;
615 struct fb_info
*fg_info
=
616 registered_fb
[con2fb_map
[fg_console
]];
618 fbcon_prepare_logo(fg_vc
, fg_info
, fg_vc
->vc_cols
,
619 fg_vc
->vc_rows
, fg_vc
->vc_cols
,
623 update_screen(vc_cons
[fg_console
].d
);
627 * set_con2fb_map - map console to frame buffer device
628 * @unit: virtual console number to map
629 * @newidx: frame buffer index to map virtual console to
630 * @user: user request
632 * Maps a virtual console @unit to a frame buffer device
635 static int set_con2fb_map(int unit
, int newidx
, int user
)
637 struct vc_data
*vc
= vc_cons
[unit
].d
;
638 int oldidx
= con2fb_map
[unit
];
639 struct fb_info
*info
= registered_fb
[newidx
];
640 struct fb_info
*oldinfo
= NULL
;
643 if (oldidx
== newidx
)
649 if (!err
&& !search_for_mapped_con()) {
651 return fbcon_takeover(0);
655 oldinfo
= registered_fb
[oldidx
];
657 found
= search_fb_in_map(newidx
);
659 acquire_console_sem();
660 con2fb_map
[unit
] = newidx
;
662 err
= con2fb_acquire_newinfo(vc
, info
, unit
, oldidx
);
666 * If old fb is not mapped to any of the consoles,
667 * fbcon should release it.
669 if (!err
&& oldinfo
&& !search_fb_in_map(oldidx
))
670 err
= con2fb_release_oldinfo(vc
, oldinfo
, info
, unit
, oldidx
,
674 int show_logo
= (fg_console
== 0 && !user
&&
675 logo_shown
!= FBCON_LOGO_DONTSHOW
);
678 con2fb_init_newinfo(info
);
679 con2fb_map_boot
[unit
] = newidx
;
680 con2fb_init_display(vc
, info
, unit
, show_logo
);
683 release_console_sem();
688 * Low Level Operations
690 /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */
691 static int var_to_display(struct display
*disp
,
692 struct fb_var_screeninfo
*var
,
693 struct fb_info
*info
)
695 disp
->xres_virtual
= var
->xres_virtual
;
696 disp
->yres_virtual
= var
->yres_virtual
;
697 disp
->bits_per_pixel
= var
->bits_per_pixel
;
698 disp
->grayscale
= var
->grayscale
;
699 disp
->nonstd
= var
->nonstd
;
700 disp
->accel_flags
= var
->accel_flags
;
701 disp
->height
= var
->height
;
702 disp
->width
= var
->width
;
703 disp
->red
= var
->red
;
704 disp
->green
= var
->green
;
705 disp
->blue
= var
->blue
;
706 disp
->transp
= var
->transp
;
707 disp
->rotate
= var
->rotate
;
708 disp
->mode
= fb_match_mode(var
, &info
->modelist
);
709 if (disp
->mode
== NULL
)
710 /* This should not happen */
715 static void display_to_var(struct fb_var_screeninfo
*var
,
716 struct display
*disp
)
718 fb_videomode_to_var(var
, disp
->mode
);
719 var
->xres_virtual
= disp
->xres_virtual
;
720 var
->yres_virtual
= disp
->yres_virtual
;
721 var
->bits_per_pixel
= disp
->bits_per_pixel
;
722 var
->grayscale
= disp
->grayscale
;
723 var
->nonstd
= disp
->nonstd
;
724 var
->accel_flags
= disp
->accel_flags
;
725 var
->height
= disp
->height
;
726 var
->width
= disp
->width
;
727 var
->red
= disp
->red
;
728 var
->green
= disp
->green
;
729 var
->blue
= disp
->blue
;
730 var
->transp
= disp
->transp
;
731 var
->rotate
= disp
->rotate
;
734 static const char *fbcon_startup(void)
736 const char *display_desc
= "frame buffer device";
737 struct display
*p
= &fb_display
[fg_console
];
738 struct vc_data
*vc
= vc_cons
[fg_console
].d
;
739 struct font_desc
*font
= NULL
;
740 struct module
*owner
;
741 struct fb_info
*info
= NULL
;
742 struct fbcon_ops
*ops
;
748 * If num_registered_fb is zero, this is a call for the dummy part.
749 * The frame buffer devices weren't initialized yet.
751 if (!num_registered_fb
|| info_idx
== -1)
754 * Instead of blindly using registered_fb[0], we use info_idx, set by
757 info
= registered_fb
[info_idx
];
761 owner
= info
->fbops
->owner
;
762 if (!try_module_get(owner
))
764 if (info
->fbops
->fb_open
&& info
->fbops
->fb_open(info
, 0)) {
769 ops
= kmalloc(sizeof(struct fbcon_ops
), GFP_KERNEL
);
775 memset(ops
, 0, sizeof(struct fbcon_ops
));
778 info
->fbcon_par
= ops
;
779 set_blitting_type(vc
, info
, NULL
);
781 if (info
->fix
.type
!= FB_TYPE_TEXT
) {
782 if (fbcon_softback_size
) {
786 kmalloc(fbcon_softback_size
,
789 fbcon_softback_size
= 0;
795 kfree((void *) softback_buf
);
801 softback_in
= softback_top
= softback_curr
=
806 /* Setup default font */
808 if (!fontname
[0] || !(font
= find_font(fontname
)))
809 font
= get_default_font(info
->var
.xres
,
811 vc
->vc_font
.width
= font
->width
;
812 vc
->vc_font
.height
= font
->height
;
813 vc
->vc_font
.data
= p
->fontdata
= font
->data
;
814 vc
->vc_font
.charcount
= 256; /* FIXME Need to support more fonts */
817 cols
= info
->var
.xres
/ vc
->vc_font
.width
;
818 rows
= info
->var
.yres
/ vc
->vc_font
.height
;
819 vc_resize(vc
, cols
, rows
);
821 DPRINTK("mode: %s\n", info
->fix
.id
);
822 DPRINTK("visual: %d\n", info
->fix
.visual
);
823 DPRINTK("res: %dx%d-%d\n", info
->var
.xres
,
825 info
->var
.bits_per_pixel
);
829 cursor_blink_rate
= ATARI_CURSOR_BLINK_RATE
;
831 request_irq(IRQ_AUTO_4
, fb_vbl_handler
,
832 IRQ_TYPE_PRIO
, "framebuffer vbl",
835 #endif /* CONFIG_ATARI */
839 * On a Macintoy, the VBL interrupt may or may not be active.
840 * As interrupt based cursor is more reliable and race free, we
841 * probe for VBL interrupts.
846 * Probe for VBL: set temp. handler ...
848 irqres
= request_irq(IRQ_MAC_VBL
, fb_vbl_detect
, 0,
849 "framebuffer vbl", info
);
853 * ... and spin for 20 ms ...
855 while (!vbl_detected
&& ++ct
< 1000)
860 ("fbcon_startup: No VBL detected, using timer based cursor.\n");
862 free_irq(IRQ_MAC_VBL
, fb_vbl_detect
);
866 * interrupt based cursor ok
868 cursor_blink_rate
= MAC_CURSOR_BLINK_RATE
;
870 request_irq(IRQ_MAC_VBL
, fb_vbl_handler
, 0,
871 "framebuffer vbl", info
);
874 * VBL not detected: fall through, use timer based cursor
879 #endif /* CONFIG_MAC */
881 #if defined(__arm__) && defined(IRQ_VSYNCPULSE)
882 cursor_blink_rate
= ARM_CURSOR_BLINK_RATE
;
883 irqres
= request_irq(IRQ_VSYNCPULSE
, fb_vbl_handler
, SA_SHIRQ
,
884 "framebuffer vbl", info
);
886 /* Initialize the work queue. If the driver provides its
887 * own work queue this means it will use something besides
888 * default timer to flash the cursor. */
889 if (!info
->queue
.func
) {
890 INIT_WORK(&info
->queue
, fb_flashcursor
, info
);
892 init_timer(&ops
->cursor_timer
);
893 ops
->cursor_timer
.function
= cursor_timer_handler
;
894 ops
->cursor_timer
.expires
= jiffies
+ HZ
/ 5;
895 ops
->cursor_timer
.data
= (unsigned long ) info
;
896 add_timer(&ops
->cursor_timer
);
901 static void fbcon_init(struct vc_data
*vc
, int init
)
903 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
904 struct fbcon_ops
*ops
;
905 struct vc_data
**default_mode
= vc
->vc_display_fg
;
906 struct vc_data
*svc
= *default_mode
;
907 struct display
*t
, *p
= &fb_display
[vc
->vc_num
];
908 int logo
= 1, new_rows
, new_cols
, rows
, cols
, charcnt
= 256;
909 int cap
= info
->flags
;
911 if (info_idx
== -1 || info
== NULL
)
913 if (vc
!= svc
|| logo_shown
== FBCON_LOGO_DONTSHOW
||
914 (info
->fix
.type
== FB_TYPE_TEXT
))
917 info
->var
.xoffset
= info
->var
.yoffset
= p
->yscroll
= 0; /* reset wrap/pan */
919 if (var_to_display(p
, &info
->var
, info
))
922 /* If we are not the first console on this
923 fb, copy the font from that console */
924 t
= &fb_display
[svc
->vc_num
];
925 if (!vc
->vc_font
.data
) {
926 vc
->vc_font
.data
= p
->fontdata
= t
->fontdata
;
927 vc
->vc_font
.width
= (*default_mode
)->vc_font
.width
;
928 vc
->vc_font
.height
= (*default_mode
)->vc_font
.height
;
929 p
->userfont
= t
->userfont
;
931 REFCOUNT(p
->fontdata
)++;
934 charcnt
= FNTCHARCNT(p
->fontdata
);
935 vc
->vc_can_do_color
= (fb_get_color_depth(&info
->var
) != 1);
936 vc
->vc_complement_mask
= vc
->vc_can_do_color
? 0x7700 : 0x0800;
937 if (charcnt
== 256) {
938 vc
->vc_hi_font_mask
= 0;
940 vc
->vc_hi_font_mask
= 0x100;
941 if (vc
->vc_can_do_color
)
942 vc
->vc_complement_mask
<<= 1;
945 if (!*svc
->vc_uni_pagedir_loc
)
946 con_set_default_unimap(svc
);
947 if (!*vc
->vc_uni_pagedir_loc
)
948 con_copy_unimap(vc
, svc
);
952 new_cols
= info
->var
.xres
/ vc
->vc_font
.width
;
953 new_rows
= info
->var
.yres
/ vc
->vc_font
.height
;
954 vc_resize(vc
, new_cols
, new_rows
);
956 ops
= info
->fbcon_par
;
958 * We must always set the mode. The mode of the previous console
959 * driver could be in the same resolution but we are using different
960 * hardware so we have to initialize the hardware.
962 * We need to do it in fbcon_init() to prevent screen corruption.
964 if (CON_IS_VISIBLE(vc
)) {
965 if (info
->fbops
->fb_set_par
&&
966 !(ops
->flags
& FBCON_FLAGS_INIT
))
967 info
->fbops
->fb_set_par(info
);
968 ops
->flags
|= FBCON_FLAGS_INIT
;
973 if ((cap
& FBINFO_HWACCEL_COPYAREA
) &&
974 !(cap
& FBINFO_HWACCEL_DISABLED
))
975 p
->scrollmode
= SCROLL_MOVE
;
976 else /* default to something safe */
977 p
->scrollmode
= SCROLL_REDRAW
;
980 * ++guenther: console.c:vc_allocate() relies on initializing
981 * vc_{cols,rows}, but we must not set those if we are only
982 * resizing the console.
985 vc
->vc_cols
= new_cols
;
986 vc
->vc_rows
= new_rows
;
990 fbcon_prepare_logo(vc
, info
, cols
, rows
, new_cols
, new_rows
);
992 if (vc
== svc
&& softback_buf
) {
993 int l
= fbcon_softback_size
/ vc
->vc_size_row
;
995 softback_end
= softback_buf
+ l
* vc
->vc_size_row
;
997 /* Smaller scrollback makes no sense, and 0 would screw
998 the operation totally */
1004 static void fbcon_deinit(struct vc_data
*vc
)
1006 struct display
*p
= &fb_display
[vc
->vc_num
];
1013 /* ====================================================================== */
1015 /* fbcon_XXX routines - interface used by the world
1017 * This system is now divided into two levels because of complications
1018 * caused by hardware scrolling. Top level functions:
1020 * fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins()
1022 * handles y values in range [0, scr_height-1] that correspond to real
1023 * screen positions. y_wrap shift means that first line of bitmap may be
1024 * anywhere on this display. These functions convert lineoffsets to
1025 * bitmap offsets and deal with the wrap-around case by splitting blits.
1027 * fbcon_bmove_physical_8() -- These functions fast implementations
1028 * fbcon_clear_physical_8() -- of original fbcon_XXX fns.
1029 * fbcon_putc_physical_8() -- (font width != 8) may be added later
1033 * At the moment fbcon_putc() cannot blit across vertical wrap boundary
1034 * Implies should only really hardware scroll in rows. Only reason for
1035 * restriction is simplicity & efficiency at the moment.
1038 static __inline__
int real_y(struct display
*p
, int ypos
)
1040 int rows
= p
->vrows
;
1043 return ypos
< rows
? ypos
: ypos
- rows
;
1047 static void fbcon_clear(struct vc_data
*vc
, int sy
, int sx
, int height
,
1050 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1051 struct fbcon_ops
*ops
= info
->fbcon_par
;
1053 struct display
*p
= &fb_display
[vc
->vc_num
];
1056 if (fbcon_is_inactive(vc
, info
))
1059 if (!height
|| !width
)
1062 /* Split blits that cross physical y_wrap boundary */
1064 y_break
= p
->vrows
- p
->yscroll
;
1065 if (sy
< y_break
&& sy
+ height
- 1 >= y_break
) {
1066 u_int b
= y_break
- sy
;
1067 ops
->clear(vc
, info
, real_y(p
, sy
), sx
, b
, width
);
1068 ops
->clear(vc
, info
, real_y(p
, sy
+ b
), sx
, height
- b
,
1071 ops
->clear(vc
, info
, real_y(p
, sy
), sx
, height
, width
);
1074 static void fbcon_putcs(struct vc_data
*vc
, const unsigned short *s
,
1075 int count
, int ypos
, int xpos
)
1077 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1078 struct display
*p
= &fb_display
[vc
->vc_num
];
1079 struct fbcon_ops
*ops
= info
->fbcon_par
;
1081 if (!fbcon_is_inactive(vc
, info
))
1082 ops
->putcs(vc
, info
, s
, count
, real_y(p
, ypos
), xpos
,
1083 get_color(vc
, info
, scr_readw(s
), 1),
1084 get_color(vc
, info
, scr_readw(s
), 0));
1087 static void fbcon_putc(struct vc_data
*vc
, int c
, int ypos
, int xpos
)
1091 scr_writew(c
, &chr
);
1092 fbcon_putcs(vc
, &chr
, 1, ypos
, xpos
);
1095 static void fbcon_clear_margins(struct vc_data
*vc
, int bottom_only
)
1097 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1098 struct fbcon_ops
*ops
= info
->fbcon_par
;
1100 if (!fbcon_is_inactive(vc
, info
))
1101 ops
->clear_margins(vc
, info
, bottom_only
);
1104 static void fbcon_cursor(struct vc_data
*vc
, int mode
)
1106 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1107 struct fbcon_ops
*ops
= info
->fbcon_par
;
1108 struct display
*p
= &fb_display
[vc
->vc_num
];
1110 int c
= scr_readw((u16
*) vc
->vc_pos
);
1112 if (fbcon_is_inactive(vc
, info
))
1115 ops
->cursor_flash
= (mode
== CM_ERASE
) ? 0 : 1;
1116 if (mode
& CM_SOFTBACK
) {
1117 mode
&= ~CM_SOFTBACK
;
1121 fbcon_set_origin(vc
);
1125 ops
->cursor(vc
, info
, p
, mode
, y
, get_color(vc
, info
, c
, 1),
1126 get_color(vc
, info
, c
, 0));
1127 vbl_cursor_cnt
= CURSOR_DRAW_DELAY
;
1130 static int scrollback_phys_max
= 0;
1131 static int scrollback_max
= 0;
1132 static int scrollback_current
= 0;
1134 static int update_var(int con
, struct fb_info
*info
)
1136 if (con
== ((struct fbcon_ops
*)info
->fbcon_par
)->currcon
)
1137 return fb_pan_display(info
, &info
->var
);
1142 * If no vc is existent yet, just set struct display
1144 static void fbcon_preset_disp(struct fb_info
*info
, struct fb_var_screeninfo
*var
,
1147 struct display
*p
= &fb_display
[unit
];
1148 struct display
*t
= &fb_display
[fg_console
];
1150 var
->xoffset
= var
->yoffset
= p
->yscroll
= 0;
1151 if (var_to_display(p
, var
, info
))
1154 p
->fontdata
= t
->fontdata
;
1155 p
->userfont
= t
->userfont
;
1157 REFCOUNT(p
->fontdata
)++;
1160 static void fbcon_set_disp(struct fb_info
*info
, struct fb_var_screeninfo
*var
,
1163 struct display
*p
= &fb_display
[vc
->vc_num
], *t
;
1164 struct vc_data
**default_mode
= vc
->vc_display_fg
;
1165 struct vc_data
*svc
= *default_mode
;
1166 int rows
, cols
, charcnt
= 256;
1168 var
->xoffset
= var
->yoffset
= p
->yscroll
= 0;
1169 if (var_to_display(p
, var
, info
))
1171 t
= &fb_display
[svc
->vc_num
];
1172 if (!vc
->vc_font
.data
) {
1173 vc
->vc_font
.data
= p
->fontdata
= t
->fontdata
;
1174 vc
->vc_font
.width
= (*default_mode
)->vc_font
.width
;
1175 vc
->vc_font
.height
= (*default_mode
)->vc_font
.height
;
1176 p
->userfont
= t
->userfont
;
1178 REFCOUNT(p
->fontdata
)++;
1181 charcnt
= FNTCHARCNT(p
->fontdata
);
1183 vc
->vc_can_do_color
= (fb_get_color_depth(var
) != 1);
1184 vc
->vc_complement_mask
= vc
->vc_can_do_color
? 0x7700 : 0x0800;
1185 if (charcnt
== 256) {
1186 vc
->vc_hi_font_mask
= 0;
1188 vc
->vc_hi_font_mask
= 0x100;
1189 if (vc
->vc_can_do_color
)
1190 vc
->vc_complement_mask
<<= 1;
1193 if (!*svc
->vc_uni_pagedir_loc
)
1194 con_set_default_unimap(svc
);
1195 if (!*vc
->vc_uni_pagedir_loc
)
1196 con_copy_unimap(vc
, svc
);
1198 cols
= var
->xres
/ vc
->vc_font
.width
;
1199 rows
= var
->yres
/ vc
->vc_font
.height
;
1200 vc_resize(vc
, cols
, rows
);
1201 if (CON_IS_VISIBLE(vc
)) {
1204 int l
= fbcon_softback_size
/ vc
->vc_size_row
;
1207 softback_end
= softback_buf
+ l
*
1210 /* Smaller scrollback makes no sense, and 0
1211 would screw the operation totally */
1218 static __inline__
void ywrap_up(struct vc_data
*vc
, int count
)
1220 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1221 struct display
*p
= &fb_display
[vc
->vc_num
];
1223 p
->yscroll
+= count
;
1224 if (p
->yscroll
>= p
->vrows
) /* Deal with wrap */
1225 p
->yscroll
-= p
->vrows
;
1226 info
->var
.xoffset
= 0;
1227 info
->var
.yoffset
= p
->yscroll
* vc
->vc_font
.height
;
1228 info
->var
.vmode
|= FB_VMODE_YWRAP
;
1229 update_var(vc
->vc_num
, info
);
1230 scrollback_max
+= count
;
1231 if (scrollback_max
> scrollback_phys_max
)
1232 scrollback_max
= scrollback_phys_max
;
1233 scrollback_current
= 0;
1236 static __inline__
void ywrap_down(struct vc_data
*vc
, int count
)
1238 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1239 struct display
*p
= &fb_display
[vc
->vc_num
];
1241 p
->yscroll
-= count
;
1242 if (p
->yscroll
< 0) /* Deal with wrap */
1243 p
->yscroll
+= p
->vrows
;
1244 info
->var
.xoffset
= 0;
1245 info
->var
.yoffset
= p
->yscroll
* vc
->vc_font
.height
;
1246 info
->var
.vmode
|= FB_VMODE_YWRAP
;
1247 update_var(vc
->vc_num
, info
);
1248 scrollback_max
-= count
;
1249 if (scrollback_max
< 0)
1251 scrollback_current
= 0;
1254 static __inline__
void ypan_up(struct vc_data
*vc
, int count
)
1256 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1257 struct display
*p
= &fb_display
[vc
->vc_num
];
1258 struct fbcon_ops
*ops
= info
->fbcon_par
;
1260 p
->yscroll
+= count
;
1261 if (p
->yscroll
> p
->vrows
- vc
->vc_rows
) {
1262 ops
->bmove(vc
, info
, p
->vrows
- vc
->vc_rows
,
1263 0, 0, 0, vc
->vc_rows
, vc
->vc_cols
);
1264 p
->yscroll
-= p
->vrows
- vc
->vc_rows
;
1266 info
->var
.xoffset
= 0;
1267 info
->var
.yoffset
= p
->yscroll
* vc
->vc_font
.height
;
1268 info
->var
.vmode
&= ~FB_VMODE_YWRAP
;
1269 update_var(vc
->vc_num
, info
);
1270 fbcon_clear_margins(vc
, 1);
1271 scrollback_max
+= count
;
1272 if (scrollback_max
> scrollback_phys_max
)
1273 scrollback_max
= scrollback_phys_max
;
1274 scrollback_current
= 0;
1277 static __inline__
void ypan_up_redraw(struct vc_data
*vc
, int t
, int count
)
1279 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1280 struct display
*p
= &fb_display
[vc
->vc_num
];
1283 p
->yscroll
+= count
;
1284 if (p
->yscroll
> p
->vrows
- vc
->vc_rows
) {
1285 p
->yscroll
-= p
->vrows
- vc
->vc_rows
;
1289 info
->var
.xoffset
= 0;
1290 info
->var
.yoffset
= p
->yscroll
* vc
->vc_font
.height
;
1291 info
->var
.vmode
&= ~FB_VMODE_YWRAP
;
1293 fbcon_redraw_move(vc
, p
, t
+ count
, vc
->vc_rows
- count
, t
);
1294 update_var(vc
->vc_num
, info
);
1295 fbcon_clear_margins(vc
, 1);
1296 scrollback_max
+= count
;
1297 if (scrollback_max
> scrollback_phys_max
)
1298 scrollback_max
= scrollback_phys_max
;
1299 scrollback_current
= 0;
1302 static __inline__
void ypan_down(struct vc_data
*vc
, int count
)
1304 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1305 struct display
*p
= &fb_display
[vc
->vc_num
];
1306 struct fbcon_ops
*ops
= info
->fbcon_par
;
1308 p
->yscroll
-= count
;
1309 if (p
->yscroll
< 0) {
1310 ops
->bmove(vc
, info
, 0, 0, p
->vrows
- vc
->vc_rows
,
1311 0, vc
->vc_rows
, vc
->vc_cols
);
1312 p
->yscroll
+= p
->vrows
- vc
->vc_rows
;
1314 info
->var
.xoffset
= 0;
1315 info
->var
.yoffset
= p
->yscroll
* vc
->vc_font
.height
;
1316 info
->var
.vmode
&= ~FB_VMODE_YWRAP
;
1317 update_var(vc
->vc_num
, info
);
1318 fbcon_clear_margins(vc
, 1);
1319 scrollback_max
-= count
;
1320 if (scrollback_max
< 0)
1322 scrollback_current
= 0;
1325 static __inline__
void ypan_down_redraw(struct vc_data
*vc
, int t
, int count
)
1327 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1328 struct display
*p
= &fb_display
[vc
->vc_num
];
1331 p
->yscroll
-= count
;
1332 if (p
->yscroll
< 0) {
1333 p
->yscroll
+= p
->vrows
- vc
->vc_rows
;
1336 info
->var
.xoffset
= 0;
1337 info
->var
.yoffset
= p
->yscroll
* vc
->vc_font
.height
;
1338 info
->var
.vmode
&= ~FB_VMODE_YWRAP
;
1340 fbcon_redraw_move(vc
, p
, t
, vc
->vc_rows
- count
, t
+ count
);
1341 update_var(vc
->vc_num
, info
);
1342 fbcon_clear_margins(vc
, 1);
1343 scrollback_max
-= count
;
1344 if (scrollback_max
< 0)
1346 scrollback_current
= 0;
1349 static void fbcon_redraw_softback(struct vc_data
*vc
, struct display
*p
,
1352 int count
= vc
->vc_rows
;
1353 unsigned short *d
, *s
;
1357 d
= (u16
*) softback_curr
;
1358 if (d
== (u16
*) softback_in
)
1359 d
= (u16
*) vc
->vc_origin
;
1360 n
= softback_curr
+ delta
* vc
->vc_size_row
;
1361 softback_lines
-= delta
;
1363 if (softback_curr
< softback_top
&& n
< softback_buf
) {
1364 n
+= softback_end
- softback_buf
;
1365 if (n
< softback_top
) {
1367 (softback_top
- n
) / vc
->vc_size_row
;
1370 } else if (softback_curr
>= softback_top
1371 && n
< softback_top
) {
1373 (softback_top
- n
) / vc
->vc_size_row
;
1377 if (softback_curr
> softback_in
&& n
>= softback_end
) {
1378 n
+= softback_buf
- softback_end
;
1379 if (n
> softback_in
) {
1383 } else if (softback_curr
<= softback_in
&& n
> softback_in
) {
1388 if (n
== softback_curr
)
1391 s
= (u16
*) softback_curr
;
1392 if (s
== (u16
*) softback_in
)
1393 s
= (u16
*) vc
->vc_origin
;
1395 unsigned short *start
;
1399 unsigned short attr
= 1;
1402 le
= advance_row(s
, 1);
1405 if (attr
!= (c
& 0xff00)) {
1408 fbcon_putcs(vc
, start
, s
- start
,
1414 if (c
== scr_readw(d
)) {
1416 fbcon_putcs(vc
, start
, s
- start
,
1429 fbcon_putcs(vc
, start
, s
- start
, line
, x
);
1431 if (d
== (u16
*) softback_end
)
1432 d
= (u16
*) softback_buf
;
1433 if (d
== (u16
*) softback_in
)
1434 d
= (u16
*) vc
->vc_origin
;
1435 if (s
== (u16
*) softback_end
)
1436 s
= (u16
*) softback_buf
;
1437 if (s
== (u16
*) softback_in
)
1438 s
= (u16
*) vc
->vc_origin
;
1442 static void fbcon_redraw_move(struct vc_data
*vc
, struct display
*p
,
1443 int line
, int count
, int dy
)
1445 unsigned short *s
= (unsigned short *)
1446 (vc
->vc_origin
+ vc
->vc_size_row
* line
);
1449 unsigned short *start
= s
;
1450 unsigned short *le
= advance_row(s
, 1);
1453 unsigned short attr
= 1;
1457 if (attr
!= (c
& 0xff00)) {
1460 fbcon_putcs(vc
, start
, s
- start
,
1466 console_conditional_schedule();
1470 fbcon_putcs(vc
, start
, s
- start
, dy
, x
);
1471 console_conditional_schedule();
1476 static void fbcon_redraw(struct vc_data
*vc
, struct display
*p
,
1477 int line
, int count
, int offset
)
1479 unsigned short *d
= (unsigned short *)
1480 (vc
->vc_origin
+ vc
->vc_size_row
* line
);
1481 unsigned short *s
= d
+ offset
;
1484 unsigned short *start
= s
;
1485 unsigned short *le
= advance_row(s
, 1);
1488 unsigned short attr
= 1;
1492 if (attr
!= (c
& 0xff00)) {
1495 fbcon_putcs(vc
, start
, s
- start
,
1501 if (c
== scr_readw(d
)) {
1503 fbcon_putcs(vc
, start
, s
- start
,
1513 console_conditional_schedule();
1518 fbcon_putcs(vc
, start
, s
- start
, line
, x
);
1519 console_conditional_schedule();
1524 /* NOTE: We subtract two lines from these pointers */
1525 s
-= vc
->vc_size_row
;
1526 d
-= vc
->vc_size_row
;
1531 static inline void fbcon_softback_note(struct vc_data
*vc
, int t
,
1536 if (vc
->vc_num
!= fg_console
)
1538 p
= (unsigned short *) (vc
->vc_origin
+ t
* vc
->vc_size_row
);
1541 scr_memcpyw((u16
*) softback_in
, p
, vc
->vc_size_row
);
1543 p
= advance_row(p
, 1);
1544 softback_in
+= vc
->vc_size_row
;
1545 if (softback_in
== softback_end
)
1546 softback_in
= softback_buf
;
1547 if (softback_in
== softback_top
) {
1548 softback_top
+= vc
->vc_size_row
;
1549 if (softback_top
== softback_end
)
1550 softback_top
= softback_buf
;
1553 softback_curr
= softback_in
;
1556 static int fbcon_scroll(struct vc_data
*vc
, int t
, int b
, int dir
,
1559 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1560 struct display
*p
= &fb_display
[vc
->vc_num
];
1561 struct fbcon_ops
*ops
= info
->fbcon_par
;
1562 int scroll_partial
= info
->flags
& FBINFO_PARTIAL_PAN_OK
;
1564 if (fbcon_is_inactive(vc
, info
))
1567 fbcon_cursor(vc
, CM_ERASE
);
1570 * ++Geert: Only use ywrap/ypan if the console is in text mode
1571 * ++Andrew: Only use ypan on hardware text mode when scrolling the
1572 * whole screen (prevents flicker).
1577 if (count
> vc
->vc_rows
) /* Maximum realistic size */
1578 count
= vc
->vc_rows
;
1580 fbcon_softback_note(vc
, t
, count
);
1581 if (logo_shown
>= 0)
1583 switch (p
->scrollmode
) {
1585 ops
->bmove(vc
, info
, t
+ count
, 0, t
, 0,
1586 b
- t
- count
, vc
->vc_cols
);
1587 ops
->clear(vc
, info
, b
- count
, 0, count
,
1591 case SCROLL_WRAP_MOVE
:
1592 if (b
- t
- count
> 3 * vc
->vc_rows
>> 2) {
1594 fbcon_bmove(vc
, 0, 0, count
, 0, t
,
1596 ywrap_up(vc
, count
);
1597 if (vc
->vc_rows
- b
> 0)
1598 fbcon_bmove(vc
, b
- count
, 0, b
, 0,
1601 } else if (info
->flags
& FBINFO_READS_FAST
)
1602 fbcon_bmove(vc
, t
+ count
, 0, t
, 0,
1603 b
- t
- count
, vc
->vc_cols
);
1606 fbcon_clear(vc
, b
- count
, 0, count
, vc
->vc_cols
);
1609 case SCROLL_PAN_REDRAW
:
1610 if ((p
->yscroll
+ count
<=
1611 2 * (p
->vrows
- vc
->vc_rows
))
1612 && ((!scroll_partial
&& (b
- t
== vc
->vc_rows
))
1615 3 * vc
->vc_rows
>> 2)))) {
1617 fbcon_redraw_move(vc
, p
, 0, t
, count
);
1618 ypan_up_redraw(vc
, t
, count
);
1619 if (vc
->vc_rows
- b
> 0)
1620 fbcon_redraw_move(vc
, p
, b
- count
,
1621 vc
->vc_rows
- b
, b
);
1623 fbcon_redraw_move(vc
, p
, t
+ count
, b
- t
- count
, t
);
1624 fbcon_clear(vc
, b
- count
, 0, count
, vc
->vc_cols
);
1627 case SCROLL_PAN_MOVE
:
1628 if ((p
->yscroll
+ count
<=
1629 2 * (p
->vrows
- vc
->vc_rows
))
1630 && ((!scroll_partial
&& (b
- t
== vc
->vc_rows
))
1633 3 * vc
->vc_rows
>> 2)))) {
1635 fbcon_bmove(vc
, 0, 0, count
, 0, t
,
1638 if (vc
->vc_rows
- b
> 0)
1639 fbcon_bmove(vc
, b
- count
, 0, b
, 0,
1642 } else if (info
->flags
& FBINFO_READS_FAST
)
1643 fbcon_bmove(vc
, t
+ count
, 0, t
, 0,
1644 b
- t
- count
, vc
->vc_cols
);
1647 fbcon_clear(vc
, b
- count
, 0, count
, vc
->vc_cols
);
1652 fbcon_redraw(vc
, p
, t
, b
- t
- count
,
1653 count
* vc
->vc_cols
);
1654 fbcon_clear(vc
, b
- count
, 0, count
, vc
->vc_cols
);
1655 scr_memsetw((unsigned short *) (vc
->vc_origin
+
1658 vc
->vc_video_erase_char
,
1659 vc
->vc_size_row
* count
);
1665 if (count
> vc
->vc_rows
) /* Maximum realistic size */
1666 count
= vc
->vc_rows
;
1667 switch (p
->scrollmode
) {
1669 ops
->bmove(vc
, info
, t
, 0, t
+ count
, 0,
1670 b
- t
- count
, vc
->vc_cols
);
1671 ops
->clear(vc
, info
, t
, 0, count
, vc
->vc_cols
);
1674 case SCROLL_WRAP_MOVE
:
1675 if (b
- t
- count
> 3 * vc
->vc_rows
>> 2) {
1676 if (vc
->vc_rows
- b
> 0)
1677 fbcon_bmove(vc
, b
, 0, b
- count
, 0,
1680 ywrap_down(vc
, count
);
1682 fbcon_bmove(vc
, count
, 0, 0, 0, t
,
1684 } else if (info
->flags
& FBINFO_READS_FAST
)
1685 fbcon_bmove(vc
, t
, 0, t
+ count
, 0,
1686 b
- t
- count
, vc
->vc_cols
);
1689 fbcon_clear(vc
, t
, 0, count
, vc
->vc_cols
);
1692 case SCROLL_PAN_MOVE
:
1693 if ((count
- p
->yscroll
<= p
->vrows
- vc
->vc_rows
)
1694 && ((!scroll_partial
&& (b
- t
== vc
->vc_rows
))
1697 3 * vc
->vc_rows
>> 2)))) {
1698 if (vc
->vc_rows
- b
> 0)
1699 fbcon_bmove(vc
, b
, 0, b
- count
, 0,
1702 ypan_down(vc
, count
);
1704 fbcon_bmove(vc
, count
, 0, 0, 0, t
,
1706 } else if (info
->flags
& FBINFO_READS_FAST
)
1707 fbcon_bmove(vc
, t
, 0, t
+ count
, 0,
1708 b
- t
- count
, vc
->vc_cols
);
1711 fbcon_clear(vc
, t
, 0, count
, vc
->vc_cols
);
1714 case SCROLL_PAN_REDRAW
:
1715 if ((count
- p
->yscroll
<= p
->vrows
- vc
->vc_rows
)
1716 && ((!scroll_partial
&& (b
- t
== vc
->vc_rows
))
1719 3 * vc
->vc_rows
>> 2)))) {
1720 if (vc
->vc_rows
- b
> 0)
1721 fbcon_redraw_move(vc
, p
, b
, vc
->vc_rows
- b
,
1723 ypan_down_redraw(vc
, t
, count
);
1725 fbcon_redraw_move(vc
, p
, count
, t
, 0);
1727 fbcon_redraw_move(vc
, p
, t
, b
- t
- count
, t
+ count
);
1728 fbcon_clear(vc
, t
, 0, count
, vc
->vc_cols
);
1733 fbcon_redraw(vc
, p
, b
- 1, b
- t
- count
,
1734 -count
* vc
->vc_cols
);
1735 fbcon_clear(vc
, t
, 0, count
, vc
->vc_cols
);
1736 scr_memsetw((unsigned short *) (vc
->vc_origin
+
1739 vc
->vc_video_erase_char
,
1740 vc
->vc_size_row
* count
);
1748 static void fbcon_bmove(struct vc_data
*vc
, int sy
, int sx
, int dy
, int dx
,
1749 int height
, int width
)
1751 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1752 struct display
*p
= &fb_display
[vc
->vc_num
];
1754 if (fbcon_is_inactive(vc
, info
))
1757 if (!width
|| !height
)
1760 /* Split blits that cross physical y_wrap case.
1761 * Pathological case involves 4 blits, better to use recursive
1762 * code rather than unrolled case
1764 * Recursive invocations don't need to erase the cursor over and
1765 * over again, so we use fbcon_bmove_rec()
1767 fbcon_bmove_rec(vc
, p
, sy
, sx
, dy
, dx
, height
, width
,
1768 p
->vrows
- p
->yscroll
);
1771 static void fbcon_bmove_rec(struct vc_data
*vc
, struct display
*p
, int sy
, int sx
,
1772 int dy
, int dx
, int height
, int width
, u_int y_break
)
1774 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1775 struct fbcon_ops
*ops
= info
->fbcon_par
;
1778 if (sy
< y_break
&& sy
+ height
> y_break
) {
1780 if (dy
< sy
) { /* Avoid trashing self */
1781 fbcon_bmove_rec(vc
, p
, sy
, sx
, dy
, dx
, b
, width
,
1783 fbcon_bmove_rec(vc
, p
, sy
+ b
, sx
, dy
+ b
, dx
,
1784 height
- b
, width
, y_break
);
1786 fbcon_bmove_rec(vc
, p
, sy
+ b
, sx
, dy
+ b
, dx
,
1787 height
- b
, width
, y_break
);
1788 fbcon_bmove_rec(vc
, p
, sy
, sx
, dy
, dx
, b
, width
,
1794 if (dy
< y_break
&& dy
+ height
> y_break
) {
1796 if (dy
< sy
) { /* Avoid trashing self */
1797 fbcon_bmove_rec(vc
, p
, sy
, sx
, dy
, dx
, b
, width
,
1799 fbcon_bmove_rec(vc
, p
, sy
+ b
, sx
, dy
+ b
, dx
,
1800 height
- b
, width
, y_break
);
1802 fbcon_bmove_rec(vc
, p
, sy
+ b
, sx
, dy
+ b
, dx
,
1803 height
- b
, width
, y_break
);
1804 fbcon_bmove_rec(vc
, p
, sy
, sx
, dy
, dx
, b
, width
,
1809 ops
->bmove(vc
, info
, real_y(p
, sy
), sx
, real_y(p
, dy
), dx
,
1813 static __inline__
void updatescrollmode(struct display
*p
, struct fb_info
*info
,
1816 int fh
= vc
->vc_font
.height
;
1817 int cap
= info
->flags
;
1818 int good_pan
= (cap
& FBINFO_HWACCEL_YPAN
)
1819 && divides(info
->fix
.ypanstep
, vc
->vc_font
.height
)
1820 && info
->var
.yres_virtual
> info
->var
.yres
;
1821 int good_wrap
= (cap
& FBINFO_HWACCEL_YWRAP
)
1822 && divides(info
->fix
.ywrapstep
, vc
->vc_font
.height
)
1823 && divides(vc
->vc_font
.height
, info
->var
.yres_virtual
);
1824 int reading_fast
= cap
& FBINFO_READS_FAST
;
1825 int fast_copyarea
= (cap
& FBINFO_HWACCEL_COPYAREA
) && !(cap
& FBINFO_HWACCEL_DISABLED
);
1826 int fast_imageblit
= (cap
& FBINFO_HWACCEL_IMAGEBLIT
) && !(cap
& FBINFO_HWACCEL_DISABLED
);
1828 p
->vrows
= info
->var
.yres_virtual
/fh
;
1829 if (info
->var
.yres
> (fh
* (vc
->vc_rows
+ 1)))
1830 p
->vrows
-= (info
->var
.yres
- (fh
* vc
->vc_rows
)) / fh
;
1831 if ((info
->var
.yres
% fh
) && (info
->var
.yres_virtual
% fh
<
1832 info
->var
.yres
% fh
))
1835 if (good_wrap
|| good_pan
) {
1836 if (reading_fast
|| fast_copyarea
)
1837 p
->scrollmode
= good_wrap
? SCROLL_WRAP_MOVE
: SCROLL_PAN_MOVE
;
1839 p
->scrollmode
= good_wrap
? SCROLL_REDRAW
:
1842 if (reading_fast
|| (fast_copyarea
&& !fast_imageblit
))
1843 p
->scrollmode
= SCROLL_MOVE
;
1845 p
->scrollmode
= SCROLL_REDRAW
;
1849 static int fbcon_resize(struct vc_data
*vc
, unsigned int width
,
1850 unsigned int height
)
1852 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1853 struct display
*p
= &fb_display
[vc
->vc_num
];
1854 struct fb_var_screeninfo var
= info
->var
;
1856 int fw
= vc
->vc_font
.width
;
1857 int fh
= vc
->vc_font
.height
;
1859 var
.xres
= width
* fw
;
1860 var
.yres
= height
* fh
;
1861 x_diff
= info
->var
.xres
- var
.xres
;
1862 y_diff
= info
->var
.yres
- var
.yres
;
1863 if (x_diff
< 0 || x_diff
> fw
|| (y_diff
< 0 || y_diff
> fh
)) {
1864 struct fb_videomode
*mode
;
1866 DPRINTK("attempting resize %ix%i\n", var
.xres
, var
.yres
);
1867 mode
= fb_find_best_mode(&var
, &info
->modelist
);
1870 fb_videomode_to_var(&var
, mode
);
1871 if (width
> var
.xres
/fw
|| height
> var
.yres
/fh
)
1874 * The following can probably have any value... Do we need to
1877 var
.bits_per_pixel
= p
->bits_per_pixel
;
1878 var
.xres_virtual
= p
->xres_virtual
;
1879 var
.yres_virtual
= p
->yres_virtual
;
1880 var
.accel_flags
= p
->accel_flags
;
1881 var
.width
= p
->width
;
1882 var
.height
= p
->height
;
1884 var
.green
= p
->green
;
1886 var
.transp
= p
->transp
;
1887 var
.nonstd
= p
->nonstd
;
1889 DPRINTK("resize now %ix%i\n", var
.xres
, var
.yres
);
1890 if (CON_IS_VISIBLE(vc
)) {
1891 var
.activate
= FB_ACTIVATE_NOW
|
1893 fb_set_var(info
, &var
);
1895 var_to_display(p
, &info
->var
, info
);
1897 updatescrollmode(p
, info
, vc
);
1901 static int fbcon_switch(struct vc_data
*vc
)
1903 struct fb_info
*info
;
1904 struct display
*p
= &fb_display
[vc
->vc_num
];
1905 struct fb_var_screeninfo var
;
1906 int i
, prev_console
;
1908 info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
1911 int l
= fbcon_softback_size
/ vc
->vc_size_row
;
1913 fbcon_set_origin(vc
);
1914 softback_top
= softback_curr
= softback_in
= softback_buf
;
1918 softback_end
= softback_buf
+ l
* vc
->vc_size_row
;
1920 /* Smaller scrollback makes no sense, and 0 would screw
1921 the operation totally */
1926 if (logo_shown
>= 0) {
1927 struct vc_data
*conp2
= vc_cons
[logo_shown
].d
;
1929 if (conp2
->vc_top
== logo_lines
1930 && conp2
->vc_bottom
== conp2
->vc_rows
)
1932 logo_shown
= FBCON_LOGO_CANSHOW
;
1935 prev_console
= ((struct fbcon_ops
*)info
->fbcon_par
)->currcon
;
1938 * FIXME: If we have multiple fbdev's loaded, we need to
1939 * update all info->currcon. Perhaps, we can place this
1940 * in a centralized structure, but this might break some
1943 * info->currcon = vc->vc_num;
1945 for (i
= 0; i
< FB_MAX
; i
++) {
1946 if (registered_fb
[i
] != NULL
&& registered_fb
[i
]->fbcon_par
) {
1947 struct fbcon_ops
*ops
= registered_fb
[i
]->fbcon_par
;
1949 ops
->currcon
= vc
->vc_num
;
1952 memset(&var
, 0, sizeof(struct fb_var_screeninfo
));
1953 display_to_var(&var
, p
);
1954 var
.activate
= FB_ACTIVATE_NOW
;
1957 * make sure we don't unnecessarily trip the memcmp()
1960 info
->var
.activate
= var
.activate
;
1961 info
->var
.yoffset
= info
->var
.xoffset
= p
->yscroll
= 0;
1962 fb_set_var(info
, &var
);
1964 if (prev_console
!= -1 &&
1965 registered_fb
[con2fb_map
[prev_console
]] != info
&&
1966 info
->fbops
->fb_set_par
)
1967 info
->fbops
->fb_set_par(info
);
1969 set_blitting_type(vc
, info
, p
);
1970 ((struct fbcon_ops
*)info
->fbcon_par
)->cursor_reset
= 1;
1972 vc
->vc_can_do_color
= (fb_get_color_depth(&info
->var
) != 1);
1973 vc
->vc_complement_mask
= vc
->vc_can_do_color
? 0x7700 : 0x0800;
1974 updatescrollmode(p
, info
, vc
);
1976 switch (p
->scrollmode
) {
1977 case SCROLL_WRAP_MOVE
:
1978 scrollback_phys_max
= p
->vrows
- vc
->vc_rows
;
1980 case SCROLL_PAN_MOVE
:
1981 case SCROLL_PAN_REDRAW
:
1982 scrollback_phys_max
= p
->vrows
- 2 * vc
->vc_rows
;
1983 if (scrollback_phys_max
< 0)
1984 scrollback_phys_max
= 0;
1987 scrollback_phys_max
= 0;
1991 scrollback_current
= 0;
1993 update_var(vc
->vc_num
, info
);
1994 fbcon_set_palette(vc
, color_table
);
1995 fbcon_clear_margins(vc
, 0);
1997 if (logo_shown
== FBCON_LOGO_DRAW
) {
1999 logo_shown
= fg_console
;
2000 /* This is protected above by initmem_freed */
2003 vc
->vc_origin
+ vc
->vc_size_row
* vc
->vc_top
,
2004 vc
->vc_size_row
* (vc
->vc_bottom
-
2011 static void fbcon_generic_blank(struct vc_data
*vc
, struct fb_info
*info
,
2015 unsigned short charmask
= vc
->vc_hi_font_mask
?
2017 unsigned short oldc
;
2019 oldc
= vc
->vc_video_erase_char
;
2020 vc
->vc_video_erase_char
&= charmask
;
2021 fbcon_clear(vc
, 0, 0, vc
->vc_rows
, vc
->vc_cols
);
2022 vc
->vc_video_erase_char
= oldc
;
2026 static int fbcon_blank(struct vc_data
*vc
, int blank
, int mode_switch
)
2028 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
2029 struct fbcon_ops
*ops
= info
->fbcon_par
;
2032 struct fb_var_screeninfo var
= info
->var
;
2037 var
.activate
= FB_ACTIVATE_NOW
| FB_ACTIVATE_FORCE
;
2038 fb_set_var(info
, &var
);
2043 if (!fbcon_is_inactive(vc
, info
)) {
2044 if (ops
->blank_state
!= blank
) {
2045 ops
->blank_state
= blank
;
2046 fbcon_cursor(vc
, blank
? CM_ERASE
: CM_DRAW
);
2047 ops
->cursor_flash
= (!blank
);
2049 if (fb_blank(info
, blank
))
2050 fbcon_generic_blank(vc
, info
, blank
);
2060 static void fbcon_free_font(struct display
*p
)
2062 if (p
->userfont
&& p
->fontdata
&& (--REFCOUNT(p
->fontdata
) == 0))
2063 kfree(p
->fontdata
- FONT_EXTRA_WORDS
* sizeof(int));
2068 static int fbcon_get_font(struct vc_data
*vc
, struct console_font
*font
)
2070 u8
*fontdata
= vc
->vc_font
.data
;
2071 u8
*data
= font
->data
;
2074 font
->width
= vc
->vc_font
.width
;
2075 font
->height
= vc
->vc_font
.height
;
2076 font
->charcount
= vc
->vc_hi_font_mask
? 512 : 256;
2080 if (font
->width
<= 8) {
2081 j
= vc
->vc_font
.height
;
2082 for (i
= 0; i
< font
->charcount
; i
++) {
2083 memcpy(data
, fontdata
, j
);
2084 memset(data
+ j
, 0, 32 - j
);
2088 } else if (font
->width
<= 16) {
2089 j
= vc
->vc_font
.height
* 2;
2090 for (i
= 0; i
< font
->charcount
; i
++) {
2091 memcpy(data
, fontdata
, j
);
2092 memset(data
+ j
, 0, 64 - j
);
2096 } else if (font
->width
<= 24) {
2097 for (i
= 0; i
< font
->charcount
; i
++) {
2098 for (j
= 0; j
< vc
->vc_font
.height
; j
++) {
2099 *data
++ = fontdata
[0];
2100 *data
++ = fontdata
[1];
2101 *data
++ = fontdata
[2];
2102 fontdata
+= sizeof(u32
);
2104 memset(data
, 0, 3 * (32 - j
));
2105 data
+= 3 * (32 - j
);
2108 j
= vc
->vc_font
.height
* 4;
2109 for (i
= 0; i
< font
->charcount
; i
++) {
2110 memcpy(data
, fontdata
, j
);
2111 memset(data
+ j
, 0, 128 - j
);
2119 static int fbcon_do_set_font(struct vc_data
*vc
, int w
, int h
,
2120 u8
* data
, int userfont
)
2122 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
2123 struct display
*p
= &fb_display
[vc
->vc_num
];
2126 char *old_data
= NULL
;
2128 if (CON_IS_VISIBLE(vc
) && softback_lines
)
2129 fbcon_set_origin(vc
);
2131 resize
= (w
!= vc
->vc_font
.width
) || (h
!= vc
->vc_font
.height
);
2133 old_data
= vc
->vc_font
.data
;
2135 cnt
= FNTCHARCNT(data
);
2138 vc
->vc_font
.data
= p
->fontdata
= data
;
2139 if ((p
->userfont
= userfont
))
2141 vc
->vc_font
.width
= w
;
2142 vc
->vc_font
.height
= h
;
2143 if (vc
->vc_hi_font_mask
&& cnt
== 256) {
2144 vc
->vc_hi_font_mask
= 0;
2145 if (vc
->vc_can_do_color
) {
2146 vc
->vc_complement_mask
>>= 1;
2147 vc
->vc_s_complement_mask
>>= 1;
2150 /* ++Edmund: reorder the attribute bits */
2151 if (vc
->vc_can_do_color
) {
2152 unsigned short *cp
=
2153 (unsigned short *) vc
->vc_origin
;
2154 int count
= vc
->vc_screenbuf_size
/ 2;
2156 for (; count
> 0; count
--, cp
++) {
2158 scr_writew(((c
& 0xfe00) >> 1) |
2161 c
= vc
->vc_video_erase_char
;
2162 vc
->vc_video_erase_char
=
2163 ((c
& 0xfe00) >> 1) | (c
& 0xff);
2166 } else if (!vc
->vc_hi_font_mask
&& cnt
== 512) {
2167 vc
->vc_hi_font_mask
= 0x100;
2168 if (vc
->vc_can_do_color
) {
2169 vc
->vc_complement_mask
<<= 1;
2170 vc
->vc_s_complement_mask
<<= 1;
2173 /* ++Edmund: reorder the attribute bits */
2175 unsigned short *cp
=
2176 (unsigned short *) vc
->vc_origin
;
2177 int count
= vc
->vc_screenbuf_size
/ 2;
2179 for (; count
> 0; count
--, cp
++) {
2180 unsigned short newc
;
2182 if (vc
->vc_can_do_color
)
2184 ((c
& 0xff00) << 1) | (c
&
2188 scr_writew(newc
, cp
);
2190 c
= vc
->vc_video_erase_char
;
2191 if (vc
->vc_can_do_color
) {
2192 vc
->vc_video_erase_char
=
2193 ((c
& 0xff00) << 1) | (c
& 0xff);
2196 vc
->vc_video_erase_char
= c
& ~0x100;
2202 /* reset wrap/pan */
2203 info
->var
.xoffset
= info
->var
.yoffset
= p
->yscroll
= 0;
2204 vc_resize(vc
, info
->var
.xres
/ w
, info
->var
.yres
/ h
);
2205 if (CON_IS_VISIBLE(vc
) && softback_buf
) {
2206 int l
= fbcon_softback_size
/ vc
->vc_size_row
;
2209 softback_buf
+ l
* vc
->vc_size_row
;
2211 /* Smaller scrollback makes no sense, and 0 would screw
2212 the operation totally */
2216 } else if (CON_IS_VISIBLE(vc
)
2217 && vc
->vc_mode
== KD_TEXT
) {
2218 fbcon_clear_margins(vc
, 0);
2222 if (old_data
&& (--REFCOUNT(old_data
) == 0))
2223 kfree(old_data
- FONT_EXTRA_WORDS
* sizeof(int));
2227 static int fbcon_copy_font(struct vc_data
*vc
, int con
)
2229 struct display
*od
= &fb_display
[con
];
2230 struct console_font
*f
= &vc
->vc_font
;
2232 if (od
->fontdata
== f
->data
)
2233 return 0; /* already the same font... */
2234 return fbcon_do_set_font(vc
, f
->width
, f
->height
, od
->fontdata
, od
->userfont
);
2238 * User asked to set font; we are guaranteed that
2239 * a) width and height are in range 1..32
2240 * b) charcount does not exceed 512
2241 * but lets not assume that, since someone might someday want to use larger
2242 * fonts. And charcount of 512 is small for unicode support.
2244 * However, user space gives the font in 32 rows , regardless of
2245 * actual font height. So a new API is needed if support for larger fonts
2246 * is ever implemented.
2249 static int fbcon_set_font(struct vc_data
*vc
, struct console_font
*font
, unsigned flags
)
2251 unsigned charcount
= font
->charcount
;
2252 int w
= font
->width
;
2253 int h
= font
->height
;
2256 u8
*new_data
, *data
= font
->data
;
2257 int pitch
= (font
->width
+7) >> 3;
2259 /* Is there a reason why fbconsole couldn't handle any charcount >256?
2260 * If not this check should be changed to charcount < 256 */
2261 if (charcount
!= 256 && charcount
!= 512)
2264 size
= h
* pitch
* charcount
;
2266 new_data
= kmalloc(FONT_EXTRA_WORDS
* sizeof(int) + size
, GFP_USER
);
2271 new_data
+= FONT_EXTRA_WORDS
* sizeof(int);
2272 FNTSIZE(new_data
) = size
;
2273 FNTCHARCNT(new_data
) = charcount
;
2274 REFCOUNT(new_data
) = 0; /* usage counter */
2275 for (i
=0; i
< charcount
; i
++) {
2276 memcpy(new_data
+ i
*h
*pitch
, data
+ i
*32*pitch
, h
*pitch
);
2279 /* Since linux has a nice crc32 function use it for counting font
2281 csum
= crc32(0, new_data
, size
);
2283 FNTSUM(new_data
) = csum
;
2284 /* Check if the same font is on some other console already */
2285 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++) {
2286 struct vc_data
*tmp
= vc_cons
[i
].d
;
2288 if (fb_display
[i
].userfont
&&
2289 fb_display
[i
].fontdata
&&
2290 FNTSUM(fb_display
[i
].fontdata
) == csum
&&
2291 FNTSIZE(fb_display
[i
].fontdata
) == size
&&
2292 tmp
->vc_font
.width
== w
&&
2293 !memcmp(fb_display
[i
].fontdata
, new_data
, size
)) {
2294 kfree(new_data
- FONT_EXTRA_WORDS
* sizeof(int));
2295 new_data
= fb_display
[i
].fontdata
;
2299 return fbcon_do_set_font(vc
, font
->width
, font
->height
, new_data
, 1);
2302 static int fbcon_set_def_font(struct vc_data
*vc
, struct console_font
*font
, char *name
)
2304 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
2305 struct font_desc
*f
;
2308 f
= get_default_font(info
->var
.xres
, info
->var
.yres
);
2309 else if (!(f
= find_font(name
)))
2312 font
->width
= f
->width
;
2313 font
->height
= f
->height
;
2314 return fbcon_do_set_font(vc
, f
->width
, f
->height
, f
->data
, 0);
2317 static u16 palette_red
[16];
2318 static u16 palette_green
[16];
2319 static u16 palette_blue
[16];
2321 static struct fb_cmap palette_cmap
= {
2322 0, 16, palette_red
, palette_green
, palette_blue
, NULL
2325 static int fbcon_set_palette(struct vc_data
*vc
, unsigned char *table
)
2327 struct fb_info
*info
= registered_fb
[con2fb_map
[vc
->vc_num
]];
2331 if (fbcon_is_inactive(vc
, info
))
2334 if (!CON_IS_VISIBLE(vc
))
2337 depth
= fb_get_color_depth(&info
->var
);
2339 for (i
= j
= 0; i
< 16; i
++) {
2341 val
= vc
->vc_palette
[j
++];
2342 palette_red
[k
] = (val
<< 8) | val
;
2343 val
= vc
->vc_palette
[j
++];
2344 palette_green
[k
] = (val
<< 8) | val
;
2345 val
= vc
->vc_palette
[j
++];
2346 palette_blue
[k
] = (val
<< 8) | val
;
2348 palette_cmap
.len
= 16;
2349 palette_cmap
.start
= 0;
2351 * If framebuffer is capable of less than 16 colors,
2352 * use default palette of fbcon.
2355 fb_copy_cmap(fb_default_cmap(1 << depth
), &palette_cmap
);
2357 return fb_set_cmap(&palette_cmap
, info
);
2360 static u16
*fbcon_screen_pos(struct vc_data
*vc
, int offset
)
2365 if (vc
->vc_num
!= fg_console
|| !softback_lines
)
2366 return (u16
*) (vc
->vc_origin
+ offset
);
2367 line
= offset
/ vc
->vc_size_row
;
2368 if (line
>= softback_lines
)
2369 return (u16
*) (vc
->vc_origin
+ offset
-
2370 softback_lines
* vc
->vc_size_row
);
2371 p
= softback_curr
+ offset
;
2372 if (p
>= softback_end
)
2373 p
+= softback_buf
- softback_end
;
2377 static unsigned long fbcon_getxy(struct vc_data
*vc
, unsigned long pos
,
2383 if (pos
>= vc
->vc_origin
&& pos
< vc
->vc_scr_end
) {
2384 unsigned long offset
= (pos
- vc
->vc_origin
) / 2;
2386 x
= offset
% vc
->vc_cols
;
2387 y
= offset
/ vc
->vc_cols
;
2388 if (vc
->vc_num
== fg_console
)
2389 y
+= softback_lines
;
2390 ret
= pos
+ (vc
->vc_cols
- x
) * 2;
2391 } else if (vc
->vc_num
== fg_console
&& softback_lines
) {
2392 unsigned long offset
= pos
- softback_curr
;
2394 if (pos
< softback_curr
)
2395 offset
+= softback_end
- softback_buf
;
2397 x
= offset
% vc
->vc_cols
;
2398 y
= offset
/ vc
->vc_cols
;
2399 ret
= pos
+ (vc
->vc_cols
- x
) * 2;
2400 if (ret
== softback_end
)
2402 if (ret
== softback_in
)
2403 ret
= vc
->vc_origin
;
2405 /* Should not happen */
2407 ret
= vc
->vc_origin
;
2416 /* As we might be inside of softback, we may work with non-contiguous buffer,
2417 that's why we have to use a separate routine. */
2418 static void fbcon_invert_region(struct vc_data
*vc
, u16
* p
, int cnt
)
2421 u16 a
= scr_readw(p
);
2422 if (!vc
->vc_can_do_color
)
2424 else if (vc
->vc_hi_font_mask
== 0x100)
2425 a
= ((a
) & 0x11ff) | (((a
) & 0xe000) >> 4) |
2426 (((a
) & 0x0e00) << 4);
2428 a
= ((a
) & 0x88ff) | (((a
) & 0x7000) >> 4) |
2429 (((a
) & 0x0700) << 4);
2431 if (p
== (u16
*) softback_end
)
2432 p
= (u16
*) softback_buf
;
2433 if (p
== (u16
*) softback_in
)
2434 p
= (u16
*) vc
->vc_origin
;
2438 static int fbcon_scrolldelta(struct vc_data
*vc
, int lines
)
2440 struct fb_info
*info
= registered_fb
[con2fb_map
[fg_console
]];
2441 struct display
*p
= &fb_display
[fg_console
];
2442 int offset
, limit
, scrollback_old
;
2445 if (vc
->vc_num
!= fg_console
)
2447 if (vc
->vc_mode
!= KD_TEXT
|| !lines
)
2449 if (logo_shown
>= 0) {
2450 struct vc_data
*conp2
= vc_cons
[logo_shown
].d
;
2452 if (conp2
->vc_top
== logo_lines
2453 && conp2
->vc_bottom
== conp2
->vc_rows
)
2455 if (logo_shown
== vc
->vc_num
) {
2461 logo_lines
* vc
->vc_size_row
;
2462 for (i
= 0; i
< logo_lines
; i
++) {
2463 if (p
== softback_top
)
2465 if (p
== softback_buf
)
2467 p
-= vc
->vc_size_row
;
2468 q
-= vc
->vc_size_row
;
2469 scr_memcpyw((u16
*) q
, (u16
*) p
,
2473 update_region(vc
, vc
->vc_origin
,
2474 logo_lines
* vc
->vc_cols
);
2476 logo_shown
= FBCON_LOGO_CANSHOW
;
2478 fbcon_cursor(vc
, CM_ERASE
| CM_SOFTBACK
);
2479 fbcon_redraw_softback(vc
, p
, lines
);
2480 fbcon_cursor(vc
, CM_DRAW
| CM_SOFTBACK
);
2484 if (!scrollback_phys_max
)
2487 scrollback_old
= scrollback_current
;
2488 scrollback_current
-= lines
;
2489 if (scrollback_current
< 0)
2490 scrollback_current
= 0;
2491 else if (scrollback_current
> scrollback_max
)
2492 scrollback_current
= scrollback_max
;
2493 if (scrollback_current
== scrollback_old
)
2496 if (fbcon_is_inactive(vc
, info
))
2499 fbcon_cursor(vc
, CM_ERASE
);
2501 offset
= p
->yscroll
- scrollback_current
;
2503 switch (p
->scrollmode
) {
2504 case SCROLL_WRAP_MOVE
:
2505 info
->var
.vmode
|= FB_VMODE_YWRAP
;
2507 case SCROLL_PAN_MOVE
:
2508 case SCROLL_PAN_REDRAW
:
2509 limit
-= vc
->vc_rows
;
2510 info
->var
.vmode
&= ~FB_VMODE_YWRAP
;
2515 else if (offset
>= limit
)
2517 info
->var
.xoffset
= 0;
2518 info
->var
.yoffset
= offset
* vc
->vc_font
.height
;
2519 update_var(vc
->vc_num
, info
);
2520 if (!scrollback_current
)
2521 fbcon_cursor(vc
, CM_DRAW
);
2525 static int fbcon_set_origin(struct vc_data
*vc
)
2528 fbcon_scrolldelta(vc
, softback_lines
);
2532 static void fbcon_suspended(struct fb_info
*info
)
2534 struct vc_data
*vc
= NULL
;
2535 struct fbcon_ops
*ops
= info
->fbcon_par
;
2537 if (!ops
|| ops
->currcon
< 0)
2539 vc
= vc_cons
[ops
->currcon
].d
;
2541 /* Clear cursor, restore saved data */
2542 fbcon_cursor(vc
, CM_ERASE
);
2545 static void fbcon_resumed(struct fb_info
*info
)
2548 struct fbcon_ops
*ops
= info
->fbcon_par
;
2550 if (!ops
|| ops
->currcon
< 0)
2552 vc
= vc_cons
[ops
->currcon
].d
;
2557 static void fbcon_modechanged(struct fb_info
*info
)
2559 struct fbcon_ops
*ops
= info
->fbcon_par
;
2564 if (!ops
|| ops
->currcon
< 0)
2566 vc
= vc_cons
[ops
->currcon
].d
;
2567 if (vc
->vc_mode
!= KD_TEXT
|| registered_fb
[con2fb_map
[ops
->currcon
]] != info
)
2570 p
= &fb_display
[vc
->vc_num
];
2572 info
->var
.xoffset
= info
->var
.yoffset
= p
->yscroll
= 0;
2574 if (CON_IS_VISIBLE(vc
)) {
2575 var_to_display(p
, &info
->var
, info
);
2576 cols
= info
->var
.xres
/ vc
->vc_font
.width
;
2577 rows
= info
->var
.yres
/ vc
->vc_font
.height
;
2578 vc_resize(vc
, cols
, rows
);
2579 updatescrollmode(p
, info
, vc
);
2581 scrollback_current
= 0;
2582 update_var(vc
->vc_num
, info
);
2583 fbcon_set_palette(vc
, color_table
);
2586 int l
= fbcon_softback_size
/ vc
->vc_size_row
;
2588 softback_end
= softback_buf
+ l
* vc
->vc_size_row
;
2590 /* Smaller scrollback makes no sense, and 0
2591 would screw the operation totally */
2598 static int fbcon_mode_deleted(struct fb_info
*info
,
2599 struct fb_videomode
*mode
)
2601 struct fb_info
*fb_info
;
2603 int i
, j
, found
= 0;
2605 /* before deletion, ensure that mode is not in use */
2606 for (i
= first_fb_vc
; i
<= last_fb_vc
; i
++) {
2610 fb_info
= registered_fb
[j
];
2611 if (fb_info
!= info
)
2616 if (fb_mode_is_equal(p
->mode
, mode
)) {
2624 static int fbcon_fb_registered(int idx
)
2628 if (info_idx
== -1) {
2629 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++) {
2630 if (con2fb_map_boot
[i
] == idx
) {
2636 ret
= fbcon_takeover(1);
2638 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++) {
2639 if (con2fb_map_boot
[i
] == idx
)
2640 set_con2fb_map(i
, idx
, 0);
2647 static void fbcon_fb_blanked(struct fb_info
*info
, int blank
)
2649 struct fbcon_ops
*ops
= info
->fbcon_par
;
2652 if (!ops
|| ops
->currcon
< 0)
2655 vc
= vc_cons
[ops
->currcon
].d
;
2656 if (vc
->vc_mode
!= KD_TEXT
||
2657 registered_fb
[con2fb_map
[ops
->currcon
]] != info
)
2660 if (CON_IS_VISIBLE(vc
)) {
2664 do_unblank_screen(0);
2666 ops
->blank_state
= blank
;
2669 static void fbcon_new_modelist(struct fb_info
*info
)
2673 struct fb_var_screeninfo var
;
2674 struct fb_videomode
*mode
;
2676 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++) {
2677 if (registered_fb
[con2fb_map
[i
]] != info
)
2679 if (!fb_display
[i
].mode
)
2682 display_to_var(&var
, &fb_display
[i
]);
2683 mode
= fb_find_nearest_mode(&var
, &info
->modelist
);
2684 fb_videomode_to_var(&var
, mode
);
2687 fbcon_set_disp(info
, &var
, vc
);
2689 fbcon_preset_disp(info
, &var
, i
);
2694 static int fbcon_event_notify(struct notifier_block
*self
,
2695 unsigned long action
, void *data
)
2697 struct fb_event
*event
= data
;
2698 struct fb_info
*info
= event
->info
;
2699 struct fb_videomode
*mode
;
2700 struct fb_con2fbmap
*con2fb
;
2704 case FB_EVENT_SUSPEND
:
2705 fbcon_suspended(info
);
2707 case FB_EVENT_RESUME
:
2708 fbcon_resumed(info
);
2710 case FB_EVENT_MODE_CHANGE
:
2711 fbcon_modechanged(info
);
2713 case FB_EVENT_MODE_DELETE
:
2715 ret
= fbcon_mode_deleted(info
, mode
);
2717 case FB_EVENT_FB_REGISTERED
:
2718 ret
= fbcon_fb_registered(info
->node
);
2720 case FB_EVENT_SET_CONSOLE_MAP
:
2721 con2fb
= event
->data
;
2722 ret
= set_con2fb_map(con2fb
->console
- 1,
2723 con2fb
->framebuffer
, 1);
2725 case FB_EVENT_GET_CONSOLE_MAP
:
2726 con2fb
= event
->data
;
2727 con2fb
->framebuffer
= con2fb_map
[con2fb
->console
- 1];
2729 case FB_EVENT_BLANK
:
2730 fbcon_fb_blanked(info
, *(int *)event
->data
);
2732 case FB_EVENT_NEW_MODELIST
:
2733 fbcon_new_modelist(info
);
2741 * The console `switch' structure for the frame buffer based console
2744 static const struct consw fb_con
= {
2745 .owner
= THIS_MODULE
,
2746 .con_startup
= fbcon_startup
,
2747 .con_init
= fbcon_init
,
2748 .con_deinit
= fbcon_deinit
,
2749 .con_clear
= fbcon_clear
,
2750 .con_putc
= fbcon_putc
,
2751 .con_putcs
= fbcon_putcs
,
2752 .con_cursor
= fbcon_cursor
,
2753 .con_scroll
= fbcon_scroll
,
2754 .con_bmove
= fbcon_bmove
,
2755 .con_switch
= fbcon_switch
,
2756 .con_blank
= fbcon_blank
,
2757 .con_font_set
= fbcon_set_font
,
2758 .con_font_get
= fbcon_get_font
,
2759 .con_font_default
= fbcon_set_def_font
,
2760 .con_font_copy
= fbcon_copy_font
,
2761 .con_set_palette
= fbcon_set_palette
,
2762 .con_scrolldelta
= fbcon_scrolldelta
,
2763 .con_set_origin
= fbcon_set_origin
,
2764 .con_invert_region
= fbcon_invert_region
,
2765 .con_screen_pos
= fbcon_screen_pos
,
2766 .con_getxy
= fbcon_getxy
,
2767 .con_resize
= fbcon_resize
,
2770 static struct notifier_block fbcon_event_notifier
= {
2771 .notifier_call
= fbcon_event_notify
,
2774 static int __init
fb_console_init(void)
2778 acquire_console_sem();
2779 fb_register_client(&fbcon_event_notifier
);
2780 release_console_sem();
2782 for (i
= 0; i
< MAX_NR_CONSOLES
; i
++)
2785 if (num_registered_fb
) {
2786 for (i
= 0; i
< FB_MAX
; i
++) {
2787 if (registered_fb
[i
] != NULL
) {
2798 module_init(fb_console_init
);
2802 static void __exit
fb_console_exit(void)
2804 acquire_console_sem();
2805 fb_unregister_client(&fbcon_event_notifier
);
2806 release_console_sem();
2807 give_up_console(&fb_con
);
2810 module_exit(fb_console_exit
);
2814 MODULE_LICENSE("GPL");