diff options
Diffstat (limited to 'drivers/video/console/fbcon.c')
-rw-r--r-- | drivers/video/console/fbcon.c | 185 |
1 files changed, 133 insertions, 52 deletions
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 35c88bd7ba5e..2e93224d2d55 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c | |||
@@ -214,7 +214,7 @@ static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info) | |||
214 | static inline int get_color(struct vc_data *vc, struct fb_info *info, | 214 | static inline int get_color(struct vc_data *vc, struct fb_info *info, |
215 | u16 c, int is_fg) | 215 | u16 c, int is_fg) |
216 | { | 216 | { |
217 | int depth = fb_get_color_depth(&info->var); | 217 | int depth = fb_get_color_depth(&info->var, &info->fix); |
218 | int color = 0; | 218 | int color = 0; |
219 | 219 | ||
220 | if (console_blanked) { | 220 | if (console_blanked) { |
@@ -230,9 +230,13 @@ static inline int get_color(struct vc_data *vc, struct fb_info *info, | |||
230 | switch (depth) { | 230 | switch (depth) { |
231 | case 1: | 231 | case 1: |
232 | { | 232 | { |
233 | int col = ~(0xfff << (max(info->var.green.length, | ||
234 | max(info->var.red.length, | ||
235 | info->var.blue.length)))) & 0xff; | ||
236 | |||
233 | /* 0 or 1 */ | 237 | /* 0 or 1 */ |
234 | int fg = (info->fix.visual != FB_VISUAL_MONO01) ? 1 : 0; | 238 | int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0; |
235 | int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : 1; | 239 | int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col; |
236 | 240 | ||
237 | if (console_blanked) | 241 | if (console_blanked) |
238 | fg = bg; | 242 | fg = bg; |
@@ -243,9 +247,25 @@ static inline int get_color(struct vc_data *vc, struct fb_info *info, | |||
243 | case 2: | 247 | case 2: |
244 | /* | 248 | /* |
245 | * Scale down 16-colors to 4 colors. Default 4-color palette | 249 | * Scale down 16-colors to 4 colors. Default 4-color palette |
246 | * is grayscale. | 250 | * is grayscale. However, simply dividing the values by 4 |
251 | * will not work, as colors 1, 2 and 3 will be scaled-down | ||
252 | * to zero rendering them invisible. So empirically convert | ||
253 | * colors to a sane 4-level grayscale. | ||
247 | */ | 254 | */ |
248 | color /= 4; | 255 | switch (color) { |
256 | case 0: | ||
257 | color = 0; /* black */ | ||
258 | break; | ||
259 | case 1 ... 6: | ||
260 | color = 2; /* white */ | ||
261 | break; | ||
262 | case 7 ... 8: | ||
263 | color = 1; /* gray */ | ||
264 | break; | ||
265 | default: | ||
266 | color = 3; /* intense white */ | ||
267 | break; | ||
268 | } | ||
249 | break; | 269 | break; |
250 | case 3: | 270 | case 3: |
251 | /* | 271 | /* |
@@ -311,6 +331,35 @@ static void cursor_timer_handler(unsigned long dev_addr) | |||
311 | mod_timer(&ops->cursor_timer, jiffies + HZ/5); | 331 | mod_timer(&ops->cursor_timer, jiffies + HZ/5); |
312 | } | 332 | } |
313 | 333 | ||
334 | static void fbcon_add_cursor_timer(struct fb_info *info) | ||
335 | { | ||
336 | struct fbcon_ops *ops = info->fbcon_par; | ||
337 | |||
338 | if ((!info->queue.func || info->queue.func == fb_flashcursor) && | ||
339 | !(ops->flags & FBCON_FLAGS_CURSOR_TIMER)) { | ||
340 | if (!info->queue.func) | ||
341 | INIT_WORK(&info->queue, fb_flashcursor, info); | ||
342 | |||
343 | init_timer(&ops->cursor_timer); | ||
344 | ops->cursor_timer.function = cursor_timer_handler; | ||
345 | ops->cursor_timer.expires = jiffies + HZ / 5; | ||
346 | ops->cursor_timer.data = (unsigned long ) info; | ||
347 | add_timer(&ops->cursor_timer); | ||
348 | ops->flags |= FBCON_FLAGS_CURSOR_TIMER; | ||
349 | } | ||
350 | } | ||
351 | |||
352 | static void fbcon_del_cursor_timer(struct fb_info *info) | ||
353 | { | ||
354 | struct fbcon_ops *ops = info->fbcon_par; | ||
355 | |||
356 | if (info->queue.func == fb_flashcursor && | ||
357 | ops->flags & FBCON_FLAGS_CURSOR_TIMER) { | ||
358 | del_timer_sync(&ops->cursor_timer); | ||
359 | ops->flags &= ~FBCON_FLAGS_CURSOR_TIMER; | ||
360 | } | ||
361 | } | ||
362 | |||
314 | #ifndef MODULE | 363 | #ifndef MODULE |
315 | static int __init fb_console_setup(char *this_opt) | 364 | static int __init fb_console_setup(char *this_opt) |
316 | { | 365 | { |
@@ -426,7 +475,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, | |||
426 | * remove underline attribute from erase character | 475 | * remove underline attribute from erase character |
427 | * if black and white framebuffer. | 476 | * if black and white framebuffer. |
428 | */ | 477 | */ |
429 | if (fb_get_color_depth(&info->var) == 1) | 478 | if (fb_get_color_depth(&info->var, &info->fix) == 1) |
430 | erase &= ~0x400; | 479 | erase &= ~0x400; |
431 | logo_height = fb_prepare_logo(info); | 480 | logo_height = fb_prepare_logo(info); |
432 | logo_lines = (logo_height + vc->vc_font.height - 1) / | 481 | logo_lines = (logo_height + vc->vc_font.height - 1) / |
@@ -563,9 +612,7 @@ static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo, | |||
563 | } | 612 | } |
564 | 613 | ||
565 | if (!err) { | 614 | if (!err) { |
566 | if (oldinfo->queue.func == fb_flashcursor) | 615 | fbcon_del_cursor_timer(oldinfo); |
567 | del_timer_sync(&ops->cursor_timer); | ||
568 | |||
569 | kfree(ops->cursor_state.mask); | 616 | kfree(ops->cursor_state.mask); |
570 | kfree(ops->cursor_data); | 617 | kfree(ops->cursor_data); |
571 | kfree(oldinfo->fbcon_par); | 618 | kfree(oldinfo->fbcon_par); |
@@ -576,22 +623,6 @@ static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo, | |||
576 | return err; | 623 | return err; |
577 | } | 624 | } |
578 | 625 | ||
579 | static void con2fb_init_newinfo(struct fb_info *info) | ||
580 | { | ||
581 | if (!info->queue.func || info->queue.func == fb_flashcursor) { | ||
582 | struct fbcon_ops *ops = info->fbcon_par; | ||
583 | |||
584 | if (!info->queue.func) | ||
585 | INIT_WORK(&info->queue, fb_flashcursor, info); | ||
586 | |||
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); | ||
592 | } | ||
593 | } | ||
594 | |||
595 | static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, | 626 | static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, |
596 | int unit, int show_logo) | 627 | int unit, int show_logo) |
597 | { | 628 | { |
@@ -675,7 +706,7 @@ static int set_con2fb_map(int unit, int newidx, int user) | |||
675 | logo_shown != FBCON_LOGO_DONTSHOW); | 706 | logo_shown != FBCON_LOGO_DONTSHOW); |
676 | 707 | ||
677 | if (!found) | 708 | if (!found) |
678 | con2fb_init_newinfo(info); | 709 | fbcon_add_cursor_timer(info); |
679 | con2fb_map_boot[unit] = newidx; | 710 | con2fb_map_boot[unit] = newidx; |
680 | con2fb_init_display(vc, info, unit, show_logo); | 711 | con2fb_init_display(vc, info, unit, show_logo); |
681 | } | 712 | } |
@@ -878,18 +909,7 @@ static const char *fbcon_startup(void) | |||
878 | } | 909 | } |
879 | #endif /* CONFIG_MAC */ | 910 | #endif /* CONFIG_MAC */ |
880 | 911 | ||
881 | /* Initialize the work queue. If the driver provides its | 912 | fbcon_add_cursor_timer(info); |
882 | * own work queue this means it will use something besides | ||
883 | * default timer to flash the cursor. */ | ||
884 | if (!info->queue.func) { | ||
885 | INIT_WORK(&info->queue, fb_flashcursor, info); | ||
886 | |||
887 | init_timer(&ops->cursor_timer); | ||
888 | ops->cursor_timer.function = cursor_timer_handler; | ||
889 | ops->cursor_timer.expires = jiffies + HZ / 5; | ||
890 | ops->cursor_timer.data = (unsigned long ) info; | ||
891 | add_timer(&ops->cursor_timer); | ||
892 | } | ||
893 | return display_desc; | 913 | return display_desc; |
894 | } | 914 | } |
895 | 915 | ||
@@ -930,7 +950,7 @@ static void fbcon_init(struct vc_data *vc, int init) | |||
930 | } | 950 | } |
931 | if (p->userfont) | 951 | if (p->userfont) |
932 | charcnt = FNTCHARCNT(p->fontdata); | 952 | charcnt = FNTCHARCNT(p->fontdata); |
933 | vc->vc_can_do_color = (fb_get_color_depth(&info->var) != 1); | 953 | vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); |
934 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; | 954 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; |
935 | if (charcnt == 256) { | 955 | if (charcnt == 256) { |
936 | vc->vc_hi_font_mask = 0; | 956 | vc->vc_hi_font_mask = 0; |
@@ -1178,7 +1198,12 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, | |||
1178 | if (p->userfont) | 1198 | if (p->userfont) |
1179 | charcnt = FNTCHARCNT(p->fontdata); | 1199 | charcnt = FNTCHARCNT(p->fontdata); |
1180 | 1200 | ||
1181 | vc->vc_can_do_color = (fb_get_color_depth(var) != 1); | 1201 | var->activate = FB_ACTIVATE_NOW; |
1202 | info->var.activate = var->activate; | ||
1203 | info->var.yoffset = info->var.xoffset = 0; | ||
1204 | fb_set_var(info, var); | ||
1205 | |||
1206 | vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); | ||
1182 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; | 1207 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; |
1183 | if (charcnt == 256) { | 1208 | if (charcnt == 256) { |
1184 | vc->vc_hi_font_mask = 0; | 1209 | vc->vc_hi_font_mask = 0; |
@@ -1898,7 +1923,7 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width, | |||
1898 | 1923 | ||
1899 | static int fbcon_switch(struct vc_data *vc) | 1924 | static int fbcon_switch(struct vc_data *vc) |
1900 | { | 1925 | { |
1901 | struct fb_info *info; | 1926 | struct fb_info *info, *old_info = NULL; |
1902 | struct display *p = &fb_display[vc->vc_num]; | 1927 | struct display *p = &fb_display[vc->vc_num]; |
1903 | struct fb_var_screeninfo var; | 1928 | struct fb_var_screeninfo var; |
1904 | int i, prev_console; | 1929 | int i, prev_console; |
@@ -1931,7 +1956,8 @@ static int fbcon_switch(struct vc_data *vc) | |||
1931 | } | 1956 | } |
1932 | 1957 | ||
1933 | prev_console = ((struct fbcon_ops *)info->fbcon_par)->currcon; | 1958 | prev_console = ((struct fbcon_ops *)info->fbcon_par)->currcon; |
1934 | 1959 | if (prev_console != -1) | |
1960 | old_info = registered_fb[con2fb_map[prev_console]]; | ||
1935 | /* | 1961 | /* |
1936 | * FIXME: If we have multiple fbdev's loaded, we need to | 1962 | * FIXME: If we have multiple fbdev's loaded, we need to |
1937 | * update all info->currcon. Perhaps, we can place this | 1963 | * update all info->currcon. Perhaps, we can place this |
@@ -1959,15 +1985,17 @@ static int fbcon_switch(struct vc_data *vc) | |||
1959 | info->var.yoffset = info->var.xoffset = p->yscroll = 0; | 1985 | info->var.yoffset = info->var.xoffset = p->yscroll = 0; |
1960 | fb_set_var(info, &var); | 1986 | fb_set_var(info, &var); |
1961 | 1987 | ||
1962 | if (prev_console != -1 && | 1988 | if (old_info != NULL && old_info != info) { |
1963 | registered_fb[con2fb_map[prev_console]] != info && | 1989 | if (info->fbops->fb_set_par) |
1964 | info->fbops->fb_set_par) | 1990 | info->fbops->fb_set_par(info); |
1965 | info->fbops->fb_set_par(info); | 1991 | fbcon_del_cursor_timer(old_info); |
1992 | fbcon_add_cursor_timer(info); | ||
1993 | } | ||
1966 | 1994 | ||
1967 | set_blitting_type(vc, info, p); | 1995 | set_blitting_type(vc, info, p); |
1968 | ((struct fbcon_ops *)info->fbcon_par)->cursor_reset = 1; | 1996 | ((struct fbcon_ops *)info->fbcon_par)->cursor_reset = 1; |
1969 | 1997 | ||
1970 | vc->vc_can_do_color = (fb_get_color_depth(&info->var) != 1); | 1998 | vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); |
1971 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; | 1999 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; |
1972 | updatescrollmode(p, info, vc); | 2000 | updatescrollmode(p, info, vc); |
1973 | 2001 | ||
@@ -2048,11 +2076,16 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) | |||
2048 | fbcon_generic_blank(vc, info, blank); | 2076 | fbcon_generic_blank(vc, info, blank); |
2049 | } | 2077 | } |
2050 | 2078 | ||
2051 | if (!blank) | 2079 | if (!blank) |
2052 | update_screen(vc); | 2080 | update_screen(vc); |
2053 | } | 2081 | } |
2054 | 2082 | ||
2055 | return 0; | 2083 | if (!blank) |
2084 | fbcon_add_cursor_timer(info); | ||
2085 | else | ||
2086 | fbcon_del_cursor_timer(info); | ||
2087 | |||
2088 | return 0; | ||
2056 | } | 2089 | } |
2057 | 2090 | ||
2058 | static void fbcon_free_font(struct display *p) | 2091 | static void fbcon_free_font(struct display *p) |
@@ -2332,7 +2365,7 @@ static int fbcon_set_palette(struct vc_data *vc, unsigned char *table) | |||
2332 | if (!CON_IS_VISIBLE(vc)) | 2365 | if (!CON_IS_VISIBLE(vc)) |
2333 | return 0; | 2366 | return 0; |
2334 | 2367 | ||
2335 | depth = fb_get_color_depth(&info->var); | 2368 | depth = fb_get_color_depth(&info->var, &info->fix); |
2336 | if (depth > 3) { | 2369 | if (depth > 3) { |
2337 | for (i = j = 0; i < 16; i++) { | 2370 | for (i = j = 0; i < 16; i++) { |
2338 | k = table[i]; | 2371 | k = table[i]; |
@@ -2593,6 +2626,51 @@ static void fbcon_modechanged(struct fb_info *info) | |||
2593 | } | 2626 | } |
2594 | } | 2627 | } |
2595 | 2628 | ||
2629 | static void fbcon_set_all_vcs(struct fb_info *info) | ||
2630 | { | ||
2631 | struct fbcon_ops *ops = info->fbcon_par; | ||
2632 | struct vc_data *vc; | ||
2633 | struct display *p; | ||
2634 | int i, rows, cols; | ||
2635 | |||
2636 | if (!ops || ops->currcon < 0) | ||
2637 | return; | ||
2638 | |||
2639 | for (i = 0; i < MAX_NR_CONSOLES; i++) { | ||
2640 | vc = vc_cons[i].d; | ||
2641 | if (!vc || vc->vc_mode != KD_TEXT || | ||
2642 | registered_fb[con2fb_map[i]] != info) | ||
2643 | continue; | ||
2644 | |||
2645 | p = &fb_display[vc->vc_num]; | ||
2646 | |||
2647 | info->var.xoffset = info->var.yoffset = p->yscroll = 0; | ||
2648 | var_to_display(p, &info->var, info); | ||
2649 | cols = info->var.xres / vc->vc_font.width; | ||
2650 | rows = info->var.yres / vc->vc_font.height; | ||
2651 | vc_resize(vc, cols, rows); | ||
2652 | |||
2653 | if (CON_IS_VISIBLE(vc)) { | ||
2654 | updatescrollmode(p, info, vc); | ||
2655 | scrollback_max = 0; | ||
2656 | scrollback_current = 0; | ||
2657 | update_var(vc->vc_num, info); | ||
2658 | fbcon_set_palette(vc, color_table); | ||
2659 | update_screen(vc); | ||
2660 | if (softback_buf) { | ||
2661 | int l = fbcon_softback_size / vc->vc_size_row; | ||
2662 | if (l > 5) | ||
2663 | softback_end = softback_buf + l * vc->vc_size_row; | ||
2664 | else { | ||
2665 | /* Smaller scrollback makes no sense, and 0 | ||
2666 | would screw the operation totally */ | ||
2667 | softback_top = 0; | ||
2668 | } | ||
2669 | } | ||
2670 | } | ||
2671 | } | ||
2672 | } | ||
2673 | |||
2596 | static int fbcon_mode_deleted(struct fb_info *info, | 2674 | static int fbcon_mode_deleted(struct fb_info *info, |
2597 | struct fb_videomode *mode) | 2675 | struct fb_videomode *mode) |
2598 | { | 2676 | { |
@@ -2708,6 +2786,9 @@ static int fbcon_event_notify(struct notifier_block *self, | |||
2708 | case FB_EVENT_MODE_CHANGE: | 2786 | case FB_EVENT_MODE_CHANGE: |
2709 | fbcon_modechanged(info); | 2787 | fbcon_modechanged(info); |
2710 | break; | 2788 | break; |
2789 | case FB_EVENT_MODE_CHANGE_ALL: | ||
2790 | fbcon_set_all_vcs(info); | ||
2791 | break; | ||
2711 | case FB_EVENT_MODE_DELETE: | 2792 | case FB_EVENT_MODE_DELETE: |
2712 | mode = event->data; | 2793 | mode = event->data; |
2713 | ret = fbcon_mode_deleted(info, mode); | 2794 | ret = fbcon_mode_deleted(info, mode); |