Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / drivers / video / console / fbcon.c
index 7ba21eb..97aff8d 100644 (file)
 #include <asm/fb.h>
 #include <asm/irq.h>
 #include <asm/system.h>
-#include <asm/uaccess.h>
 #ifdef CONFIG_ATARI
 #include <asm/atariints.h>
 #endif
 #ifdef CONFIG_MAC
 #include <asm/macints.h>
 #endif
-#if defined(__mc68000__) || defined(CONFIG_APUS)
+#if defined(__mc68000__)
 #include <asm/machdep.h>
 #include <asm/setup.h>
 #endif
@@ -93,7 +92,7 @@
 #include "fbcon.h"
 
 #ifdef FBCONDEBUG
-#  define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#  define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
 #else
 #  define DPRINTK(fmt, args...)
 #endif
@@ -127,8 +126,20 @@ static int last_fb_vc = MAX_NR_CONSOLES - 1;
 static int fbcon_is_default = 1; 
 static int fbcon_has_exited;
 static int primary_device = -1;
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
 static int map_override;
 
+static inline void fbcon_map_override(void)
+{
+       map_override = 1;
+}
+#else
+static inline void fbcon_map_override(void)
+{
+}
+#endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */
+
 /* font data */
 static char fontname[40];
 
@@ -136,7 +147,7 @@ static char fontname[40];
 static int info_idx = -1;
 
 /* console rotation */
-static int rotate;
+static int initial_rotation;
 static int fbcon_has_sysfs;
 
 static const struct consw fb_con;
@@ -192,9 +203,7 @@ static __inline__ void ypan_down(struct vc_data *vc, int count);
 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
                            int dy, int dx, int height, int width, u_int y_break);
 static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
-                          struct vc_data *vc);
-static void fbcon_preset_disp(struct fb_info *info, struct fb_var_screeninfo *var,
-                             int unit);
+                          int unit);
 static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
                              int line, int count, int dy);
 static void fbcon_modechanged(struct fb_info *info);
@@ -325,10 +334,7 @@ static inline int get_color(struct vc_data *vc, struct fb_info *info,
        switch (depth) {
        case 1:
        {
-               int col = ~(0xfff << (max(info->var.green.length,
-                                         max(info->var.red.length,
-                                             info->var.blue.length)))) & 0xff;
-
+               int col = mono_col(info);
                /* 0 or 1 */
                int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0;
                int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col;
@@ -508,7 +514,7 @@ static int __init fb_console_setup(char *this_opt)
                                                (options[j++]-'0') % FB_MAX;
                                }
 
-                               map_override = 1;
+                               fbcon_map_override();
                        }
 
                        return 1;
@@ -528,9 +534,9 @@ static int __init fb_console_setup(char *this_opt)
                if (!strncmp(options, "rotate:", 7)) {
                        options += 7;
                        if (*options)
-                               rotate = simple_strtoul(options, &options, 0);
-                       if (rotate > 3)
-                               rotate = 0;
+                               initial_rotation = simple_strtoul(options, &options, 0);
+                       if (initial_rotation > 3)
+                               initial_rotation = 0;
                }
        }
        return 1;
@@ -614,8 +620,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
        if (fb_get_color_depth(&info->var, &info->fix) == 1)
                erase &= ~0x400;
        logo_height = fb_prepare_logo(info, ops->rotate);
-       logo_lines = (logo_height + vc->vc_font.height - 1) /
-               vc->vc_font.height;
+       logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
        q = (unsigned short *) (vc->vc_origin +
                                vc->vc_size_row * rows);
        step = logo_lines * cols;
@@ -745,7 +750,9 @@ static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info,
 
        if (!err) {
                info->fbcon_par = ops;
-               set_blitting_type(vc, info);
+
+               if (vc)
+                       set_blitting_type(vc, info);
        }
 
        if (err) {
@@ -807,11 +814,7 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,
 
        ops->flags |= FBCON_FLAGS_INIT;
        ops->graphics = 0;
-
-       if (vc)
-               fbcon_set_disp(info, &info->var, vc);
-       else
-               fbcon_preset_disp(info, &info->var, unit);
+       fbcon_set_disp(info, &info->var, unit);
 
        if (show_logo) {
                struct vc_data *fg_vc = vc_cons[fg_console].d;
@@ -982,7 +985,7 @@ static const char *fbcon_startup(void)
        ops->graphics = 1;
        ops->cur_rotate = -1;
        info->fbcon_par = ops;
-       p->con_rotate = rotate;
+       p->con_rotate = initial_rotation;
        set_blitting_type(vc, info);
 
        if (info->fix.type != FB_TYPE_TEXT) {
@@ -1116,6 +1119,9 @@ static void fbcon_init(struct vc_data *vc, int init)
        if (var_to_display(p, &info->var, info))
                return;
 
+       if (!info->fbcon_par)
+               con2fb_acquire_newinfo(vc, info, vc->vc_num, -1);
+
        /* If we are not the first console on this
           fb, copy the font from that console */
        t = &fb_display[fg_console];
@@ -1166,7 +1172,7 @@ static void fbcon_init(struct vc_data *vc, int init)
                con_copy_unimap(vc, svc);
 
        ops = info->fbcon_par;
-       p->con_rotate = rotate;
+       p->con_rotate = initial_rotation;
        set_blitting_type(vc, info);
 
        cols = vc->vc_cols;
@@ -1382,36 +1388,29 @@ static int scrollback_phys_max = 0;
 static int scrollback_max = 0;
 static int scrollback_current = 0;
 
-/*
- * If no vc is existent yet, just set struct display
- */
-static void fbcon_preset_disp(struct fb_info *info, struct fb_var_screeninfo *var,
-                             int unit)
-{
-       struct display *p = &fb_display[unit];
-       struct display *t = &fb_display[fg_console];
-
-       if (var_to_display(p, var, info))
-               return;
-
-       p->fontdata = t->fontdata;
-       p->userfont = t->userfont;
-       if (p->userfont)
-               REFCOUNT(p->fontdata)++;
-}
-
 static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
-                          struct vc_data *vc)
+                          int unit)
 {
-       struct display *p = &fb_display[vc->vc_num], *t;
-       struct vc_data **default_mode = vc->vc_display_fg;
-       struct vc_data *svc = *default_mode;
+       struct display *p, *t;
+       struct vc_data **default_mode, *vc;
+       struct vc_data *svc;
        struct fbcon_ops *ops = info->fbcon_par;
        int rows, cols, charcnt = 256;
 
+       p = &fb_display[unit];
+
        if (var_to_display(p, var, info))
                return;
+
+       vc = vc_cons[unit].d;
+
+       if (!vc)
+               return;
+
+       default_mode = vc->vc_display_fg;
+       svc = *default_mode;
        t = &fb_display[svc->vc_num];
+
        if (!vc->vc_font.data) {
                vc->vc_font.data = (void *)(p->fontdata = t->fontdata);
                vc->vc_font.width = (*default_mode)->vc_font.width;
@@ -1854,6 +1853,8 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
        struct display *p = &fb_display[vc->vc_num];
        int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
+       unsigned short saved_ec;
+       int ret;
 
        if (fbcon_is_inactive(vc, info))
                return -EINVAL;
@@ -1866,6 +1867,11 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
         *           whole screen (prevents flicker).
         */
 
+       saved_ec = vc->vc_video_erase_char;
+       vc->vc_video_erase_char = vc->vc_scrl_erase_char;
+
+       ret = 0;
+
        switch (dir) {
        case SM_UP:
                if (count > vc->vc_rows)        /* Maximum realistic size */
@@ -1882,9 +1888,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
                        scr_memsetw((unsigned short *) (vc->vc_origin +
                                                        vc->vc_size_row *
                                                        (b - count)),
-                                   vc->vc_video_erase_char,
+                                   vc->vc_scrl_erase_char,
                                    vc->vc_size_row * count);
-                       return 1;
+                       ret = 1;
                        break;
 
                case SCROLL_WRAP_MOVE:
@@ -1954,9 +1960,10 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
                        scr_memsetw((unsigned short *) (vc->vc_origin +
                                                        vc->vc_size_row *
                                                        (b - count)),
-                                   vc->vc_video_erase_char,
+                                   vc->vc_scrl_erase_char,
                                    vc->vc_size_row * count);
-                       return 1;
+                       ret = 1;
+                       break;
                }
                break;
 
@@ -1973,9 +1980,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
                        scr_memsetw((unsigned short *) (vc->vc_origin +
                                                        vc->vc_size_row *
                                                        t),
-                                   vc->vc_video_erase_char,
+                                   vc->vc_scrl_erase_char,
                                    vc->vc_size_row * count);
-                       return 1;
+                       ret = 1;
                        break;
 
                case SCROLL_WRAP_MOVE:
@@ -2043,12 +2050,15 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
                        scr_memsetw((unsigned short *) (vc->vc_origin +
                                                        vc->vc_size_row *
                                                        t),
-                                   vc->vc_video_erase_char,
+                                   vc->vc_scrl_erase_char,
                                    vc->vc_size_row * count);
-                       return 1;
+                       ret = 1;
+                       break;
                }
+               break;
        }
-       return 0;
+       vc->vc_video_erase_char = saved_ec;
+       return ret;
 }
 
 
@@ -2165,7 +2175,7 @@ static __inline__ void updatescrollmode(struct display *p,
 }
 
 static int fbcon_resize(struct vc_data *vc, unsigned int width, 
-                       unsigned int height)
+                       unsigned int height, unsigned int user)
 {
        struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
        struct fbcon_ops *ops = info->fbcon_par;
@@ -2265,9 +2275,7 @@ static int fbcon_switch(struct vc_data *vc)
         * in fb_set_var()
         */
        info->var.activate = var.activate;
-       var.yoffset = info->var.yoffset;
-       var.xoffset = info->var.xoffset;
-       var.vmode = info->var.vmode;
+       var.vmode |= info->var.vmode & ~FB_VMODE_MASK;
        fb_set_var(info, &var);
        ops->var = info->var;
 
@@ -2402,7 +2410,7 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
                        update_screen(vc);
        }
 
-       if (fbcon_is_inactive(vc, info) ||
+       if (mode_switch || fbcon_is_inactive(vc, info) ||
            ops->blank_state != FB_BLANK_UNBLANK)
                fbcon_del_cursor_timer(info);
        else
@@ -2508,6 +2516,9 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
                        c = vc->vc_video_erase_char;
                        vc->vc_video_erase_char =
                            ((c & 0xfe00) >> 1) | (c & 0xff);
+                       c = vc->vc_def_color;
+                       vc->vc_scrl_erase_char =
+                           ((c & 0xFE00) >> 1) | (c & 0xFF);
                        vc->vc_attr >>= 1;
                }
        } else if (!vc->vc_hi_font_mask && cnt == 512) {
@@ -2538,9 +2549,14 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
                        if (vc->vc_can_do_color) {
                                vc->vc_video_erase_char =
                                    ((c & 0xff00) << 1) | (c & 0xff);
+                               c = vc->vc_def_color;
+                               vc->vc_scrl_erase_char =
+                                   ((c & 0xFF00) << 1) | (c & 0xFF);
                                vc->vc_attr <<= 1;
-                       } else
+                       } else {
                                vc->vc_video_erase_char = c & ~0x100;
+                               vc->vc_scrl_erase_char = c & ~0x100;
+                       }
                }
 
        }
@@ -2792,7 +2808,7 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines)
 {
        struct fb_info *info = registered_fb[con2fb_map[fg_console]];
        struct fbcon_ops *ops = info->fbcon_par;
-       struct display *p = &fb_display[fg_console];
+       struct display *disp = &fb_display[fg_console];
        int offset, limit, scrollback_old;
 
        if (softback_top) {
@@ -2830,7 +2846,7 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines)
                        logo_shown = FBCON_LOGO_CANSHOW;
                }
                fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK);
-               fbcon_redraw_softback(vc, p, lines);
+               fbcon_redraw_softback(vc, disp, lines);
                fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK);
                return 0;
        }
@@ -2852,9 +2868,9 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines)
 
        fbcon_cursor(vc, CM_ERASE);
 
-       offset = p->yscroll - scrollback_current;
-       limit = p->vrows;
-       switch (p->scrollmode) {
+       offset = disp->yscroll - scrollback_current;
+       limit = disp->vrows;
+       switch (disp->scrollmode) {
        case SCROLL_WRAP_MOVE:
                info->var.vmode |= FB_VMODE_YWRAP;
                break;
@@ -3011,6 +3027,45 @@ static int fbcon_mode_deleted(struct fb_info *info,
        return found;
 }
 
+#ifdef CONFIG_VT_HW_CONSOLE_BINDING
+static int fbcon_unbind(void)
+{
+       int ret;
+
+       ret = unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc,
+                               fbcon_is_default);
+       return ret;
+}
+#else
+static inline int fbcon_unbind(void)
+{
+       return -EINVAL;
+}
+#endif /* CONFIG_VT_HW_CONSOLE_BINDING */
+
+static int fbcon_fb_unbind(int idx)
+{
+       int i, new_idx = -1, ret = 0;
+
+       for (i = first_fb_vc; i <= last_fb_vc; i++) {
+               if (con2fb_map[i] != idx &&
+                   con2fb_map[i] != -1) {
+                       new_idx = i;
+                       break;
+               }
+       }
+
+       if (new_idx != -1) {
+               for (i = first_fb_vc; i <= last_fb_vc; i++) {
+                       if (con2fb_map[i] == idx)
+                               set_con2fb_map(i, new_idx, 0);
+               }
+       } else
+               ret = fbcon_unbind();
+
+       return ret;
+}
+
 static int fbcon_fb_unregistered(struct fb_info *info)
 {
        int i, idx = info->node;
@@ -3049,52 +3104,32 @@ static int fbcon_fb_unregistered(struct fb_info *info)
 }
 
 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
-static int fbcon_select_primary(struct fb_info *info)
+static void fbcon_select_primary(struct fb_info *info)
 {
-       int ret = 0;
-
        if (!map_override && primary_device == -1 &&
            fb_is_primary_device(info)) {
-               int i, err;
+               int i;
 
-               printk(KERN_INFO "fbcon: %s is primary device\n",
-                      info->fix.id);
+               printk(KERN_INFO "fbcon: %s (fb%i) is primary device\n",
+                      info->fix.id, info->node);
                primary_device = info->node;
 
-               if (!con_is_bound(&fb_con))
-                       goto done;
-
-               printk(KERN_INFO "fbcon: Unbinding old driver\n");
-               unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc,
-                                 fbcon_is_default);
-               info_idx = primary_device;
-
-               for (i = first_fb_vc; i <= last_fb_vc; i++) {
+               for (i = first_fb_vc; i <= last_fb_vc; i++)
                        con2fb_map_boot[i] = primary_device;
-                       con2fb_map[i] = primary_device;
-               }
-
-               printk(KERN_INFO "fbcon: Selecting new driver\n");
-               err = bind_con_driver(&fb_con, first_fb_vc, last_fb_vc,
-                                     fbcon_is_default);
 
-               if (err) {
-                       for (i = first_fb_vc; i <= last_fb_vc; i++)
-                               con2fb_map[i] = -1;
-
-                       info_idx = -1;
+               if (con_is_bound(&fb_con)) {
+                       printk(KERN_INFO "fbcon: Remapping primary device, "
+                              "fb%i, to tty %i-%i\n", info->node,
+                              first_fb_vc + 1, last_fb_vc + 1);
+                       info_idx = primary_device;
                }
-
-               ret = 1;
        }
 
-done:
-       return ret;
 }
 #else
-static inline int fbcon_select_primary(struct fb_info *info)
+static inline void fbcon_select_primary(struct fb_info *info)
 {
-       return 0;
+       return;
 }
 #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */
 
@@ -3102,9 +3137,7 @@ static int fbcon_fb_registered(struct fb_info *info)
 {
        int ret = 0, i, idx = info->node;
 
-       if (fbcon_select_primary(info))
-               goto done;
-
+       fbcon_select_primary(info);
 
        if (info_idx == -1) {
                for (i = first_fb_vc; i <= last_fb_vc; i++) {
@@ -3118,13 +3151,11 @@ static int fbcon_fb_registered(struct fb_info *info)
                        ret = fbcon_takeover(1);
        } else {
                for (i = first_fb_vc; i <= last_fb_vc; i++) {
-                       if (con2fb_map_boot[i] == idx &&
-                           con2fb_map[i] == -1)
+                       if (con2fb_map_boot[i] == idx)
                                set_con2fb_map(i, idx, 0);
                }
        }
 
-done:
        return ret;
 }
 
@@ -3167,12 +3198,7 @@ static void fbcon_new_modelist(struct fb_info *info)
                mode = fb_find_nearest_mode(fb_display[i].mode,
                                            &info->modelist);
                fb_videomode_to_var(&var, mode);
-
-               if (vc)
-                       fbcon_set_disp(info, &var, vc);
-               else
-                       fbcon_preset_disp(info, &var, i);
-
+               fbcon_set_disp(info, &var, vc->vc_num);
        }
 }
 
@@ -3247,6 +3273,9 @@ static int fbcon_event_notify(struct notifier_block *self,
                mode = event->data;
                ret = fbcon_mode_deleted(info, mode);
                break;
+       case FB_EVENT_FB_UNBIND:
+               ret = fbcon_fb_unbind(info->node);
+               break;
        case FB_EVENT_FB_REGISTERED:
                ret = fbcon_fb_registered(info);
                break;