LCOV - code coverage report
Current view: top level - drivers/video/console - fbcon.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 2018 2065 97.7 %
Date: 2017-01-25 Functions: 85 88 96.6 %

          Line data    Source code
       1             : /*
       2             :  *  linux/drivers/video/fbcon.c -- Low level frame buffer based console driver
       3             :  *
       4             :  *      Copyright (C) 1995 Geert Uytterhoeven
       5             :  *
       6             :  *
       7             :  *  This file is based on the original Amiga console driver (amicon.c):
       8             :  *
       9             :  *      Copyright (C) 1993 Hamish Macdonald
      10             :  *                         Greg Harp
      11             :  *      Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk]
      12             :  *
      13             :  *            with work by William Rucklidge (wjr@cs.cornell.edu)
      14             :  *                         Geert Uytterhoeven
      15             :  *                         Jes Sorensen (jds@kom.auc.dk)
      16             :  *                         Martin Apel
      17             :  *
      18             :  *  and on the original Atari console driver (atacon.c):
      19             :  *
      20             :  *      Copyright (C) 1993 Bjoern Brauel
      21             :  *                         Roman Hodek
      22             :  *
      23             :  *            with work by Guenther Kelleter
      24             :  *                         Martin Schaller
      25             :  *                         Andreas Schwab
      26             :  *
      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)
      31             :  *
      32             :  *  Random hacking by Martin Mares <mj@ucw.cz>
      33             :  *
      34             :  *      2001 - Documented with DocBook
      35             :  *      - Brad Douglas <brad@neruo.com>
      36             :  *
      37             :  *  The low level operations for the various display memory organizations are
      38             :  *  now in separate source files.
      39             :  *
      40             :  *  Currently the following organizations are supported:
      41             :  *
      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
      46             :  *    o mfb                     Monochrome
      47             :  *    o vga                     VGA characters/attributes
      48             :  *
      49             :  *  To do:
      50             :  *
      51             :  *    - Implement 16 plane mode (iplan2p16)
      52             :  *
      53             :  *
      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
      56             :  *  more details.
      57             :  */
      58             : 
      59             : #undef FBCONDEBUG
      60             : 
      61             : #include <linux/module.h>
      62             : #include <linux/types.h>
      63             : #include <linux/fs.h>
      64             : #include <linux/kernel.h>
      65             : #include <linux/delay.h>  /* MSch: for IRQ probe */
      66             : #include <linux/console.h>
      67             : #include <linux/string.h>
      68             : #include <linux/kd.h>
      69             : #include <linux/slab.h>
      70             : #include <linux/fb.h>
      71             : #include <linux/vt_kern.h>
      72             : #include <linux/selection.h>
      73             : #include <linux/font.h>
      74             : #include <linux/smp.h>
      75             : #include <linux/init.h>
      76             : #include <linux/interrupt.h>
      77             : #include <linux/crc32.h> /* For counting font checksums */
      78             : #include <asm/fb.h>
      79             : #include <asm/irq.h>
      80             : #include <asm/system.h>
      81             : 
      82             : #include "fbcon.h"
      83             : 
      84             : #ifdef FBCONDEBUG
      85             : #  define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
      86             : #else
      87             : #  define DPRINTK(fmt, args...)
      88             : #endif
      89             : 
      90             : enum {
      91             :         FBCON_LOGO_CANSHOW      = -1,   /* the logo can be shown */
      92             :         FBCON_LOGO_DRAW         = -2,   /* draw the logo to a console */
      93             :         FBCON_LOGO_DONTSHOW     = -3    /* do not show the logo */
      94             : };
      95             : 
      96           1 : static struct display fb_display[MAX_NR_CONSOLES];
      97             : 
      98           1 : static signed char con2fb_map[MAX_NR_CONSOLES];
      99           1 : static signed char con2fb_map_boot[MAX_NR_CONSOLES];
     100             : 
     101           1 : static int logo_lines;
     102             : /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO
     103             :    enums.  */
     104           1 : static int logo_shown = FBCON_LOGO_CANSHOW;
     105             : /* Software scrollback */
     106           1 : static int fbcon_softback_size = 32768;
     107           2 : static unsigned long softback_buf, softback_curr;
     108           1 : static unsigned long softback_in;
     109           2 : static unsigned long softback_top, softback_end;
     110           1 : static int softback_lines;
     111             : /* console mappings */
     112           1 : static int first_fb_vc;
     113           1 : static int last_fb_vc = MAX_NR_CONSOLES - 1;
     114           1 : static int fbcon_is_default = 1; 
     115           1 : static int fbcon_has_exited;
     116           1 : static int primary_device = -1;
     117           1 : static int fbcon_has_console_bind;
     118             : 
     119             : #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
     120           1 : static int map_override;
     121             : 
     122             : static inline void fbcon_map_override(void)
     123             : {
     124           1 :         map_override = 1;
     125           1 : }
     126             : #else
     127             : static inline void fbcon_map_override(void)
     128             : {
     129             : }
     130             : #endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */
     131             : 
     132             : /* font data */
     133           1 : static char fontname[40];
     134             : 
     135             : /* current fb_info */
     136           1 : static int info_idx = -1;
     137             : 
     138             : /* console rotation */
     139           1 : static int initial_rotation;
     140           1 : static int fbcon_has_sysfs;
     141             : 
     142           1 : static const struct consw fb_con;
     143             : 
     144             : #define CM_SOFTBACK     (8)
     145             : 
     146             : #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
     147             : 
     148             : static int fbcon_set_origin(struct vc_data *);
     149             : 
     150             : #define CURSOR_DRAW_DELAY               (1)
     151             : 
     152           1 : static int vbl_cursor_cnt;
     153           1 : static int fbcon_cursor_noblink;
     154             : 
     155             : #define divides(a, b)   ((!(a) || (b)%(a)) ? 0 : 1)
     156             : 
     157             : /*
     158             :  *  Interface used by the world
     159             :  */
     160             : 
     161             : static const char *fbcon_startup(void);
     162             : static void fbcon_init(struct vc_data *vc, int init);
     163             : static void fbcon_deinit(struct vc_data *vc);
     164             : static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
     165             :                         int width);
     166             : static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos);
     167             : static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
     168             :                         int count, int ypos, int xpos);
     169             : static void fbcon_clear_margins(struct vc_data *vc, int bottom_only);
     170             : static void fbcon_cursor(struct vc_data *vc, int mode);
     171             : static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
     172             :                         int count);
     173             : static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
     174             :                         int height, int width);
     175             : static int fbcon_switch(struct vc_data *vc);
     176             : static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch);
     177             : static int fbcon_set_palette(struct vc_data *vc, unsigned char *table);
     178             : static int fbcon_scrolldelta(struct vc_data *vc, int lines);
     179             : 
     180             : /*
     181             :  *  Internal routines
     182             :  */
     183             : static __inline__ void ywrap_up(struct vc_data *vc, int count);
     184             : static __inline__ void ywrap_down(struct vc_data *vc, int count);
     185             : static __inline__ void ypan_up(struct vc_data *vc, int count);
     186             : static __inline__ void ypan_down(struct vc_data *vc, int count);
     187             : static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
     188             :                             int dy, int dx, int height, int width, u_int y_break);
     189             : static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
     190             :                            int unit);
     191             : static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
     192             :                               int line, int count, int dy);
     193             : static void fbcon_modechanged(struct fb_info *info);
     194             : static void fbcon_set_all_vcs(struct fb_info *info);
     195             : static void fbcon_start(void);
     196             : static void fbcon_exit(void);
     197           1 : static struct device *fbcon_device;
     198             : 
     199             : #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
     200             : static inline void fbcon_set_rotation(struct fb_info *info)
     201             : {
     202             :         struct fbcon_ops *ops = info->fbcon_par;
     203             : 
     204             :         if (!(info->flags & FBINFO_MISC_TILEBLITTING) &&
     205             :             ops->p->con_rotate < 4)
     206             :                 ops->rotate = ops->p->con_rotate;
     207             :         else
     208             :                 ops->rotate = 0;
     209             : }
     210             : 
     211             : static void fbcon_rotate(struct fb_info *info, u32 rotate)
     212             : {
     213             :         struct fbcon_ops *ops= info->fbcon_par;
     214             :         struct fb_info *fb_info;
     215             : 
     216             :         if (!ops || ops->currcon == -1)
     217             :                 return;
     218             : 
     219             :         fb_info = registered_fb[con2fb_map[ops->currcon]];
     220             : 
     221             :         if (info == fb_info) {
     222             :                 struct display *p = &fb_display[ops->currcon];
     223             : 
     224             :                 if (rotate < 4)
     225             :                         p->con_rotate = rotate;
     226             :                 else
     227             :                         p->con_rotate = 0;
     228             : 
     229             :                 fbcon_modechanged(info);
     230             :         }
     231             : }
     232             : 
     233             : static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
     234             : {
     235             :         struct fbcon_ops *ops = info->fbcon_par;
     236             :         struct vc_data *vc;
     237             :         struct display *p;
     238             :         int i;
     239             : 
     240             :         if (!ops || ops->currcon < 0 || rotate > 3)
     241             :                 return;
     242             : 
     243             :         for (i = first_fb_vc; i <= last_fb_vc; i++) {
     244             :                 vc = vc_cons[i].d;
     245             :                 if (!vc || vc->vc_mode != KD_TEXT ||
     246             :                     registered_fb[con2fb_map[i]] != info)
     247             :                         continue;
     248             : 
     249             :                 p = &fb_display[vc->vc_num];
     250             :                 p->con_rotate = rotate;
     251             :         }
     252             : 
     253             :         fbcon_set_all_vcs(info);
     254             : }
     255             : #else
     256             : static inline void fbcon_set_rotation(struct fb_info *info)
     257             : {
     258          60 :         struct fbcon_ops *ops = info->fbcon_par;
     259             : 
     260          20 :         ops->rotate = FB_ROTATE_UR;
     261          20 : }
     262             : 
     263             : static void fbcon_rotate(struct fb_info *info, u32 rotate)
     264             : {
     265           1 :         return;
     266             : }
     267             : 
     268             : static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
     269             : {
     270           1 :         return;
     271             : }
     272             : #endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */
     273             : 
     274             : static int fbcon_get_rotate(struct fb_info *info)
     275             : {
     276           3 :         struct fbcon_ops *ops = info->fbcon_par;
     277             : 
     278           6 :         return (ops) ? ops->rotate : 0;
     279             : }
     280             : 
     281             : static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
     282             : {
     283         870 :         struct fbcon_ops *ops = info->fbcon_par;
     284             : 
     285        2610 :         return (info->state != FBINFO_STATE_RUNNING ||
     286             :                 vc->vc_mode != KD_TEXT || ops->graphics);
     287             : }
     288             : 
     289             : static inline int get_color(struct vc_data *vc, struct fb_info *info,
     290             :               u16 c, int is_fg)
     291             : {
     292        1266 :         int depth = fb_get_color_depth(&info->var, &info->fix);
     293         844 :         int color = 0;
     294         422 : 
     295        1266 :         if (console_blanked) {
     296        3376 :                 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
     297         422 : 
     298        1266 :                 c = vc->vc_video_erase_char & charmask;
     299         422 :         }
     300             : 
     301         844 :         if (depth != 1)
     302        7596 :                 color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c)
     303             :                         : attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c);
     304             : 
     305         422 :         switch (depth) {
     306        1688 :         case 1:
     307             :         {
     308        1266 :                 int col = mono_col(info);
     309             :                 /* 0 or 1 */
     310        2532 :                 int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0;
     311        2532 :                 int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col;
     312             : 
     313         844 :                 if (console_blanked)
     314         422 :                         fg = bg;
     315             : 
     316        2532 :                 color = (is_fg) ? fg : bg;
     317         422 :                 break;
     318         422 :         }
     319        1266 :         case 2:
     320             :                 /*
     321             :                  * Scale down 16-colors to 4 colors. Default 4-color palette
     322             :                  * is grayscale. However, simply dividing the values by 4
     323             :                  * will not work, as colors 1, 2 and 3 will be scaled-down
     324             :                  * to zero rendering them invisible.  So empirically convert
     325             :                  * colors to a sane 4-level grayscale.
     326             :                  */
     327             :                 switch (color) {
     328        1266 :                 case 0:
     329         422 :                         color = 0; /* black */
     330         422 :                         break;
     331        5908 :                 case 1 ... 6:
     332         844 :                         color = 2; /* white */
     333         844 :                         break;
     334        2954 :                 case 7 ... 8:
     335        1266 :                         color = 1; /* gray */
     336         844 :                         break;
     337         844 :                 default:
     338         844 :                         color = 3; /* intense white */
     339         422 :                         break;
     340             :                 }
     341         422 :                 break;
     342        1688 :         case 3:
     343             :                 /*
     344             :                  * Last 8 entries of default 16-color palette is a more intense
     345             :                  * version of the first 8 (i.e., same chrominance, different
     346             :                  * luminance).
     347             :                  */
     348         422 :                 color &= 7;
     349         422 :                 break;
     350         422 :         }
     351             : 
     352         422 : 
     353         844 :         return color;
     354             : }
     355             : 
     356             : static void fbcon_update_softback(struct vc_data *vc)
     357             : {
     358          68 :         int l = fbcon_softback_size / vc->vc_size_row;
     359             : 
     360          68 :         if (l > 5)
     361          34 :                 softback_end = softback_buf + l * vc->vc_size_row;
     362             :         else
     363             :                 /* Smaller scrollback makes no sense, and 0 would screw
     364             :                    the operation totally */
     365          34 :                 softback_top = 0;
     366          34 : }
     367             : 
     368             : static void fb_flashcursor(struct work_struct *work)
     369             : {
     370           0 :         struct fb_info *info = container_of(work, struct fb_info, queue);
     371           0 :         struct fbcon_ops *ops = info->fbcon_par;
     372           0 :         struct display *p;
     373           0 :         struct vc_data *vc = NULL;
     374           0 :         int c;
     375           0 :         int mode;
     376           0 : 
     377           0 :         acquire_console_sem();
     378           0 :         if (ops && ops->currcon != -1)
     379           0 :                 vc = vc_cons[ops->currcon].d;
     380             : 
     381           0 :         if (!vc || !CON_IS_VISIBLE(vc) ||
     382             :             registered_fb[con2fb_map[vc->vc_num]] != info ||
     383             :             vc->vc_deccm != 1) {
     384           0 :                 release_console_sem();
     385           0 :                 return;
     386             :         }
     387             : 
     388           0 :         p = &fb_display[vc->vc_num];
     389           0 :         c = scr_readw((u16 *) vc->vc_pos);
     390           0 :         mode = (!ops->cursor_flash || ops->cursor_state.enable) ?
     391             :                 CM_ERASE : CM_DRAW;
     392           0 :         ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1),
     393             :                     get_color(vc, info, c, 0));
     394           0 :         release_console_sem();
     395           0 : }
     396             : 
     397             : static void cursor_timer_handler(unsigned long dev_addr)
     398             : {
     399           0 :         struct fb_info *info = (struct fb_info *) dev_addr;
     400           0 :         struct fbcon_ops *ops = info->fbcon_par;
     401             : 
     402           0 :         schedule_work(&info->queue);
     403           0 :         mod_timer(&ops->cursor_timer, jiffies + HZ/5);
     404           0 : }
     405             : 
     406             : static void fbcon_add_cursor_timer(struct fb_info *info)
     407             : {
     408         150 :         struct fbcon_ops *ops = info->fbcon_par;
     409          50 : 
     410         400 :         if ((!info->queue.func || info->queue.func == fb_flashcursor) &&
     411             :             !(ops->flags & FBCON_FLAGS_CURSOR_TIMER) &&
     412             :             !fbcon_cursor_noblink) {
     413         100 :                 if (!info->queue.func)
     414         350 :                         INIT_WORK(&info->queue, fb_flashcursor);
     415             : 
     416         100 :                 init_timer(&ops->cursor_timer);
     417         100 :                 ops->cursor_timer.function = cursor_timer_handler;
     418         100 :                 ops->cursor_timer.expires = jiffies + HZ / 5;
     419         100 :                 ops->cursor_timer.data = (unsigned long ) info;
     420         100 :                 add_timer(&ops->cursor_timer);
     421         100 :                 ops->flags |= FBCON_FLAGS_CURSOR_TIMER;
     422             :         }
     423         100 : }
     424             : 
     425             : static void fbcon_del_cursor_timer(struct fb_info *info)
     426             : {
     427         150 :         struct fbcon_ops *ops = info->fbcon_par;
     428             : 
     429         200 :         if (info->queue.func == fb_flashcursor &&
     430             :             ops->flags & FBCON_FLAGS_CURSOR_TIMER) {
     431         100 :                 del_timer_sync(&ops->cursor_timer);
     432          50 :                 ops->flags &= ~FBCON_FLAGS_CURSOR_TIMER;
     433             :         }
     434         100 : }
     435             : 
     436             : #ifndef MODULE
     437             : static int __init fb_console_setup(char *this_opt)
     438             : {
     439           1 :         char *options;
     440           1 :         int i, j;
     441           1 : 
     442           5 :         if (!this_opt || !*this_opt)
     443           2 :                 return 1;
     444           1 : 
     445           5 :         while ((options = strsep(&this_opt, ",")) != NULL) {
     446           5 :                 if (!strncmp(options, "font:", 5))
     447           3 :                         strcpy(fontname, options + 5);
     448           1 :                 
     449           4 :                 if (!strncmp(options, "scrollback:", 11)) {
     450           2 :                         options += 11;
     451           3 :                         if (*options) {
     452           3 :                                 fbcon_softback_size = simple_strtoul(options, &options, 0);
     453           4 :                                 if (*options == 'k' || *options == 'K') {
     454           1 :                                         fbcon_softback_size *= 1024;
     455           1 :                                         options++;
     456             :                                 }
     457           2 :                                 if (*options != ',')
     458           1 :                                         return 1;
     459           1 :                                 options++;
     460             :                         } else
     461           1 :                                 return 1;
     462             :                 }
     463             :                 
     464           3 :                 if (!strncmp(options, "map:", 4)) {
     465           1 :                         options += 4;
     466           2 :                         if (*options) {
     467           6 :                                 for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
     468           4 :                                         if (!options[j])
     469           2 :                                                 j = 0;
     470           3 :                                         con2fb_map_boot[i] =
     471             :                                                 (options[j++]-'0') % FB_MAX;
     472             :                                 }
     473             : 
     474           2 :                                 fbcon_map_override();
     475             :                         }
     476             : 
     477           2 :                         return 1;
     478             :                 }
     479             : 
     480           3 :                 if (!strncmp(options, "vc:", 3)) {
     481           1 :                         options += 3;
     482           2 :                         if (*options)
     483           2 :                                 first_fb_vc = simple_strtoul(options, &options, 10) - 1;
     484           2 :                         if (first_fb_vc < 0)
     485           1 :                                 first_fb_vc = 0;
     486           4 :                         if (*options++ == '-')
     487           2 :                                 last_fb_vc = simple_strtoul(options, &options, 10) - 1;
     488           1 :                         fbcon_is_default = 0; 
     489             :                 }       
     490             : 
     491           3 :                 if (!strncmp(options, "rotate:", 7)) {
     492           1 :                         options += 7;
     493           2 :                         if (*options)
     494           2 :                                 initial_rotation = simple_strtoul(options, &options, 0);
     495           2 :                         if (initial_rotation > 3)
     496           1 :                                 initial_rotation = 0;
     497             :                 }
     498             :         }
     499           1 :         return 1;
     500             : }
     501             : 
     502             : __setup("fbcon=", fb_console_setup);
     503           1 : #endif
     504             : 
     505             : static int search_fb_in_map(int idx)
     506             : {
     507          54 :         int i, retval = 0;
     508          27 : 
     509         135 :         for (i = first_fb_vc; i <= last_fb_vc; i++) {
     510         108 :                 if (con2fb_map[i] == idx)
     511          54 :                         retval = 1;
     512             :         }
     513          27 :         return retval;
     514             : }
     515             : 
     516             : static int search_for_mapped_con(void)
     517             : {
     518           6 :         int i, retval = 0;
     519           3 : 
     520          15 :         for (i = first_fb_vc; i <= last_fb_vc; i++) {
     521          12 :                 if (con2fb_map[i] != -1)
     522           6 :                         retval = 1;
     523             :         }
     524           3 :         return retval;
     525             : }
     526             : 
     527             : static int fbcon_takeover(int show_logo)
     528             : {
     529           5 :         int err, i;
     530           5 : 
     531          10 :         if (!num_registered_fb)
     532           5 :                 return -ENODEV;
     533             : 
     534          10 :         if (!show_logo)
     535           5 :                 logo_shown = FBCON_LOGO_DONTSHOW;
     536             : 
     537          25 :         for (i = first_fb_vc; i <= last_fb_vc; i++)
     538          15 :                 con2fb_map[i] = info_idx;
     539           5 : 
     540           5 :         err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
     541             :                                 fbcon_is_default);
     542             : 
     543          10 :         if (err) {
     544          25 :                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
     545          15 :                         con2fb_map[i] = -1;
     546           5 :                 }
     547           5 :                 info_idx = -1;
     548             :         } else {
     549           5 :                 fbcon_has_console_bind = 1;
     550             :         }
     551             : 
     552          10 :         return err;
     553             : }
     554             : 
     555             : #ifdef MODULE
     556             : static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
     557             :                                int cols, int rows, int new_cols, int new_rows)
     558             : {
     559             :         logo_shown = FBCON_LOGO_DONTSHOW;
     560             : }
     561             : #else
     562             : static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
     563             :                                int cols, int rows, int new_cols, int new_rows)
     564             : {
     565          16 :         /* Need to make room for the logo */
     566          48 :         struct fbcon_ops *ops = info->fbcon_par;
     567          48 :         int cnt, erase = vc->vc_video_erase_char, step;
     568          32 :         unsigned short *save = NULL, *r, *q;
     569          16 :         int logo_height;
     570          16 : 
     571          48 :         if (info->flags & FBINFO_MODULE) {
     572          32 :                 logo_shown = FBCON_LOGO_DONTSHOW;
     573          32 :                 return;
     574          16 :         }
     575          16 : 
     576          16 :         /*
     577             :          * remove underline attribute from erase character
     578             :          * if black and white framebuffer.
     579             :          */
     580          48 :         if (fb_get_color_depth(&info->var, &info->fix) == 1)
     581          16 :                 erase &= ~0x400;
     582          16 :         logo_height = fb_prepare_logo(info, ops->rotate);
     583          16 :         logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
     584          16 :         q = (unsigned short *) (vc->vc_origin +
     585             :                                 vc->vc_size_row * rows);
     586          16 :         step = logo_lines * cols;
     587          80 :         for (r = q - logo_lines * cols; r < q; r++)
     588          80 :                 if (scr_readw(r) != vc->vc_video_erase_char)
     589          32 :                         break;
     590          64 :         if (r != q && new_rows >= rows + logo_lines) {
     591          48 :                 save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL);
     592          32 :                 if (save) {
     593         112 :                         int i = cols < new_cols ? cols : new_cols;
     594          32 :                         scr_memsetw(save, erase, logo_lines * new_cols * 2);
     595          16 :                         r = q - step;
     596          96 :                         for (cnt = 0; cnt < logo_lines; cnt++, r += i)
     597          32 :                                 scr_memcpyw(save + cnt * new_cols, r, 2 * i);
     598          48 :                         r = q;
     599             :                 }
     600             :         }
     601          96 :         if (r == q) {
     602             :                 /* We can scroll screen down */
     603          48 :                 r = q - step - cols;
     604         144 :                 for (cnt = rows - logo_lines; cnt > 0; cnt--) {
     605          48 :                         scr_memcpyw(r + step, r, vc->vc_size_row);
     606          48 :                         r -= cols;
     607             :                 }
     608          32 :                 if (!save) {
     609             :                         int lines;
     610          32 :                         if (vc->vc_y + logo_lines >= rows)
     611          16 :                                 lines = rows - vc->vc_y - 1;
     612             :                         else
     613          16 :                                 lines = logo_lines;
     614          16 :                         vc->vc_y += lines;
     615          16 :                         vc->vc_pos += lines * vc->vc_size_row;
     616             :                 }
     617             :         }
     618         192 :         scr_memsetw((unsigned short *) vc->vc_origin,
     619             :                     erase,
     620             :                     vc->vc_size_row * logo_lines);
     621             : 
     622          80 :         if (CON_IS_VISIBLE(vc) && vc->vc_mode == KD_TEXT) {
     623          32 :                 fbcon_clear_margins(vc, 0);
     624          16 :                 update_screen(vc);
     625             :         }
     626             : 
     627          64 :         if (save) {
     628          32 :                 q = (unsigned short *) (vc->vc_origin +
     629             :                                         vc->vc_size_row *
     630             :                                         rows);
     631          32 :                 scr_memcpyw(q, save, logo_lines * new_cols * 2);
     632          32 :                 vc->vc_y += logo_lines;
     633          32 :                 vc->vc_pos += logo_lines * vc->vc_size_row;
     634          32 :                 kfree(save);
     635             :         }
     636             : 
     637          64 :         if (logo_lines > vc->vc_bottom) {
     638          32 :                 logo_shown = FBCON_LOGO_CANSHOW;
     639          32 :                 printk(KERN_INFO
     640             :                        "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
     641          64 :         } else if (logo_shown != FBCON_LOGO_DONTSHOW) {
     642          32 :                 logo_shown = FBCON_LOGO_DRAW;
     643          32 :                 vc->vc_top = logo_lines;
     644             :         }
     645          32 : }
     646             : #endif /* MODULE */
     647             : 
     648             : #ifdef CONFIG_FB_TILEBLITTING
     649             : static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
     650             : {
     651             :         struct fbcon_ops *ops = info->fbcon_par;
     652             : 
     653             :         ops->p = &fb_display[vc->vc_num];
     654             : 
     655             :         if ((info->flags & FBINFO_MISC_TILEBLITTING))
     656             :                 fbcon_set_tileops(vc, info);
     657             :         else {
     658             :                 fbcon_set_rotation(info);
     659             :                 fbcon_set_bitops(ops);
     660             :         }
     661             : }
     662             : 
     663             : static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
     664             : {
     665             :         int err = 0;
     666             : 
     667             :         if (info->flags & FBINFO_MISC_TILEBLITTING &&
     668             :             info->tileops->fb_get_tilemax(info) < charcount)
     669             :                 err = 1;
     670             : 
     671             :         return err;
     672             : }
     673             : #else
     674             : static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
     675             : {
     676          60 :         struct fbcon_ops *ops = info->fbcon_par;
     677             : 
     678          20 :         info->flags &= ~FBINFO_MISC_TILEBLITTING;
     679          40 :         ops->p = &fb_display[vc->vc_num];
     680          40 :         fbcon_set_rotation(info);
     681          20 :         fbcon_set_bitops(ops);
     682          20 : }
     683             : 
     684             : static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
     685             : {
     686           1 :         return 0;
     687             : }
     688             : 
     689             : #endif /* CONFIG_MISC_TILEBLITTING */
     690             : 
     691             : 
     692             : static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info,
     693             :                                   int unit, int oldidx)
     694             : {
     695           8 :         struct fbcon_ops *ops = NULL;
     696           8 :         int err = 0;
     697           4 : 
     698          20 :         if (!try_module_get(info->fbops->owner))
     699           8 :                 err = -ENODEV;
     700             : 
     701          32 :         if (!err && info->fbops->fb_open &&
     702             :             info->fbops->fb_open(info, 0))
     703           4 :                 err = -ENODEV;
     704             : 
     705           8 :         if (!err) {
     706          12 :                 ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
     707           8 :                 if (!ops)
     708           4 :                         err = -ENOMEM;
     709             :         }
     710             : 
     711          16 :         if (!err) {
     712           8 :                 info->fbcon_par = ops;
     713             : 
     714          16 :                 if (vc)
     715          16 :                         set_blitting_type(vc, info);
     716             :         }
     717             : 
     718          24 :         if (err) {
     719          12 :                 con2fb_map[unit] = oldidx;
     720          24 :                 module_put(info->fbops->owner);
     721             :         }
     722             : 
     723          16 :         return err;
     724             : }
     725             : 
     726             : static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,
     727             :                                   struct fb_info *newinfo, int unit,
     728             :                                   int oldidx, int found)
     729           3 : {
     730           9 :         struct fbcon_ops *ops = oldinfo->fbcon_par;
     731           6 :         int err = 0, ret;
     732           3 : 
     733          18 :         if (oldinfo->fbops->fb_release &&
     734             :             oldinfo->fbops->fb_release(oldinfo, 0)) {
     735           3 :                 con2fb_map[unit] = oldidx;
     736          15 :                 if (!found && newinfo->fbops->fb_release)
     737           3 :                         newinfo->fbops->fb_release(newinfo, 0);
     738           6 :                 if (!found)
     739           6 :                         module_put(newinfo->fbops->owner);
     740           6 :                 err = -ENODEV;
     741             :         }
     742             : 
     743          12 :         if (!err) {
     744          18 :                 fbcon_del_cursor_timer(oldinfo);
     745           3 :                 kfree(ops->cursor_state.mask);
     746           6 :                 kfree(ops->cursor_data);
     747           6 :                 kfree(ops->fontbuffer);
     748           6 :                 kfree(oldinfo->fbcon_par);
     749           3 :                 oldinfo->fbcon_par = NULL;
     750           6 :                 module_put(oldinfo->fbops->owner);
     751             :                 /*
     752             :                   If oldinfo and newinfo are driving the same hardware,
     753             :                   the fb_release() method of oldinfo may attempt to
     754             :                   restore the hardware state.  This will leave the
     755             :                   newinfo in an undefined state. Thus, a call to
     756             :                   fb_set_par() may be needed for the newinfo.
     757             :                 */
     758           9 :                 if (newinfo->fbops->fb_set_par) {
     759           3 :                         ret = newinfo->fbops->fb_set_par(newinfo);
     760             : 
     761           6 :                         if (ret)
     762           3 :                                 printk(KERN_ERR "con2fb_release_oldinfo: "
     763             :                                         "detected unhandled fb_set_par error, "
     764             :                                         "error code %d\n", ret);
     765             :                 }
     766             :         }
     767             : 
     768           9 :         return err;
     769             : }
     770             : 
     771             : static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,
     772             :                                 int unit, int show_logo)
     773             : {
     774          45 :         struct fbcon_ops *ops = info->fbcon_par;
     775          15 :         int ret;
     776          15 : 
     777          30 :         ops->currcon = fg_console;
     778             : 
     779          75 :         if (info->fbops->fb_set_par && !(ops->flags & FBCON_FLAGS_INIT)) {
     780          15 :                 ret = info->fbops->fb_set_par(info);
     781             : 
     782          30 :                 if (ret)
     783          15 :                         printk(KERN_ERR "con2fb_init_display: detected "
     784             :                                 "unhandled fb_set_par error, "
     785             :                                 "error code %d\n", ret);
     786             :         }
     787             : 
     788          15 :         ops->flags |= FBCON_FLAGS_INIT;
     789          15 :         ops->graphics = 0;
     790          45 :         fbcon_set_disp(info, &info->var, unit);
     791             : 
     792          30 :         if (show_logo) {
     793          15 :                 struct vc_data *fg_vc = vc_cons[fg_console].d;
     794          15 :                 struct fb_info *fg_info =
     795             :                         registered_fb[con2fb_map[fg_console]];
     796             : 
     797         120 :                 fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols,
     798             :                                    fg_vc->vc_rows, fg_vc->vc_cols,
     799             :                                    fg_vc->vc_rows);
     800             :         }
     801             : 
     802          30 :         update_screen(vc_cons[fg_console].d);
     803          30 : }
     804             : 
     805             : /**
     806             :  *      set_con2fb_map - map console to frame buffer device
     807             :  *      @unit: virtual console number to map
     808             :  *      @newidx: frame buffer index to map virtual console to
     809             :  *      @user: user request
     810             :  *
     811             :  *      Maps a virtual console @unit to a frame buffer device
     812             :  *      @newidx.
     813             :  */
     814             : static int set_con2fb_map(int unit, int newidx, int user)
     815             : {
     816           6 :         struct vc_data *vc = vc_cons[unit].d;
     817           6 :         int oldidx = con2fb_map[unit];
     818           6 :         struct fb_info *info = registered_fb[newidx];
     819           6 :         struct fb_info *oldinfo = NULL;
     820           6 :         int found, err = 0;
     821           3 : 
     822           9 :         if (oldidx == newidx)
     823           6 :                 return 0;
     824           3 : 
     825          15 :         if (!info || fbcon_has_exited)
     826           6 :                 return -EINVAL;
     827             : 
     828          18 :         if (!err && !search_for_mapped_con()) {
     829           3 :                 info_idx = newidx;
     830          15 :                 return fbcon_takeover(0);
     831             :         }
     832             : 
     833          12 :         if (oldidx != -1)
     834           6 :                 oldinfo = registered_fb[oldidx];
     835             : 
     836          12 :         found = search_fb_in_map(newidx);
     837             : 
     838           3 :         acquire_console_sem();
     839           3 :         con2fb_map[unit] = newidx;
     840           6 :         if (!err && !found)
     841          15 :                 err = con2fb_acquire_newinfo(vc, info, unit, oldidx);
     842             : 
     843             : 
     844             :         /*
     845             :          * If old fb is not mapped to any of the consoles,
     846             :          * fbcon should release it.
     847             :          */
     848          42 :         if (!err && oldinfo && !search_fb_in_map(oldidx))
     849          12 :                 err = con2fb_release_oldinfo(vc, oldinfo, info, unit, oldidx,
     850             :                                              found);
     851             : 
     852          24 :         if (!err) {
     853          72 :                 int show_logo = (fg_console == 0 && !user &&
     854             :                                  logo_shown != FBCON_LOGO_DONTSHOW);
     855             : 
     856          24 :                 if (!found)
     857          36 :                         fbcon_add_cursor_timer(info);
     858          15 :                 con2fb_map_boot[unit] = newidx;
     859          45 :                 con2fb_init_display(vc, info, unit, show_logo);
     860             :         }
     861             : 
     862          36 :         if (!search_fb_in_map(info_idx))
     863           3 :                 info_idx = newidx;
     864             : 
     865           3 :         release_console_sem();
     866           3 :         return err;
     867             : }
     868             : 
     869             : /*
     870             :  *  Low Level Operations
     871             :  */
     872             : /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */
     873             : static int var_to_display(struct display *disp,
     874             :                           struct fb_var_screeninfo *var,
     875             :                           struct fb_info *info)
     876             : {
     877          21 :         disp->xres_virtual = var->xres_virtual;
     878          21 :         disp->yres_virtual = var->yres_virtual;
     879          21 :         disp->bits_per_pixel = var->bits_per_pixel;
     880          21 :         disp->grayscale = var->grayscale;
     881          21 :         disp->nonstd = var->nonstd;
     882          21 :         disp->accel_flags = var->accel_flags;
     883          21 :         disp->height = var->height;
     884          21 :         disp->width = var->width;
     885          21 :         disp->red = var->red;
     886          21 :         disp->green = var->green;
     887          21 :         disp->blue = var->blue;
     888          21 :         disp->transp = var->transp;
     889          21 :         disp->rotate = var->rotate;
     890          21 :         disp->mode = fb_match_mode(var, &info->modelist);
     891          63 :         if (disp->mode == NULL)
     892             :                 /* This should not happen */
     893          21 :                 return -EINVAL;
     894          21 :         return 0;
     895             : }
     896             : 
     897             : static void display_to_var(struct fb_var_screeninfo *var,
     898             :                            struct display *disp)
     899             : {
     900           3 :         fb_videomode_to_var(var, disp->mode);
     901           3 :         var->xres_virtual = disp->xres_virtual;
     902           3 :         var->yres_virtual = disp->yres_virtual;
     903           3 :         var->bits_per_pixel = disp->bits_per_pixel;
     904           3 :         var->grayscale = disp->grayscale;
     905           3 :         var->nonstd = disp->nonstd;
     906           3 :         var->accel_flags = disp->accel_flags;
     907           3 :         var->height = disp->height;
     908           3 :         var->width = disp->width;
     909           3 :         var->red = disp->red;
     910           3 :         var->green = disp->green;
     911           3 :         var->blue = disp->blue;
     912           3 :         var->transp = disp->transp;
     913           3 :         var->rotate = disp->rotate;
     914           3 : }
     915             : 
     916             : static const char *fbcon_startup(void)
     917             : {
     918           2 :         const char *display_desc = "frame buffer device";
     919           2 :         struct display *p = &fb_display[fg_console];
     920           2 :         struct vc_data *vc = vc_cons[fg_console].d;
     921           2 :         const struct font_desc *font = NULL;
     922           1 :         struct module *owner;
     923           2 :         struct fb_info *info = NULL;
     924           1 :         struct fbcon_ops *ops;
     925           1 :         int rows, cols;
     926           1 : 
     927           1 :         /*
     928           1 :          *  If num_registered_fb is zero, this is a call for the dummy part.
     929           1 :          *  The frame buffer devices weren't initialized yet.
     930           1 :          */
     931           5 :         if (!num_registered_fb || info_idx == -1)
     932           2 :                 return display_desc;
     933           1 :         /*
     934           1 :          * Instead of blindly using registered_fb[0], we use info_idx, set by
     935           1 :          * fb_console_init();
     936             :          */
     937           1 :         info = registered_fb[info_idx];
     938           2 :         if (!info)
     939           1 :                 return NULL;
     940             :         
     941           1 :         owner = info->fbops->owner;
     942           4 :         if (!try_module_get(owner))
     943           1 :                 return NULL;
     944           6 :         if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) {
     945           2 :                 module_put(owner);
     946           1 :                 return NULL;
     947             :         }
     948             : 
     949           3 :         ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
     950           2 :         if (!ops) {
     951           2 :                 module_put(owner);
     952           1 :                 return NULL;
     953             :         }
     954             : 
     955           1 :         ops->currcon = -1;
     956           1 :         ops->graphics = 1;
     957           1 :         ops->cur_rotate = -1;
     958           1 :         info->fbcon_par = ops;
     959           1 :         p->con_rotate = initial_rotation;
     960           2 :         set_blitting_type(vc, info);
     961             : 
     962           2 :         if (info->fix.type != FB_TYPE_TEXT) {
     963           2 :                 if (fbcon_softback_size) {
     964           2 :                         if (!softback_buf) {
     965           3 :                                 softback_buf =
     966             :                                     (unsigned long)
     967             :                                     kmalloc(fbcon_softback_size,
     968             :                                             GFP_KERNEL);
     969           2 :                                 if (!softback_buf) {
     970           1 :                                         fbcon_softback_size = 0;
     971           1 :                                         softback_top = 0;
     972             :                                 }
     973             :                         }
     974             :                 } else {
     975           2 :                         if (softback_buf) {
     976           1 :                                 kfree((void *) softback_buf);
     977           1 :                                 softback_buf = 0;
     978           1 :                                 softback_top = 0;
     979             :                         }
     980             :                 }
     981           4 :                 if (softback_buf)
     982           6 :                         softback_in = softback_top = softback_curr =
     983             :                             softback_buf;
     984           2 :                 softback_lines = 0;
     985             :         }
     986             : 
     987             :         /* Setup default font */
     988           6 :         if (!p->fontdata) {
     989          10 :                 if (!fontname[0] || !(font = find_font(fontname)))
     990           4 :                         font = get_default_font(info->var.xres,
     991             :                                                 info->var.yres,
     992             :                                                 info->pixmap.blit_x,
     993             :                                                 info->pixmap.blit_y);
     994           4 :                 vc->vc_font.width = font->width;
     995           4 :                 vc->vc_font.height = font->height;
     996           8 :                 vc->vc_font.data = (void *)(p->fontdata = font->data);
     997           2 :                 vc->vc_font.charcount = 256; /* FIXME  Need to support more fonts */
     998             :         }
     999             : 
    1000          20 :         cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
    1001          20 :         rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
    1002           2 :         cols /= vc->vc_font.width;
    1003           2 :         rows /= vc->vc_font.height;
    1004           2 :         vc_resize(vc, cols, rows);
    1005             : 
    1006             :         DPRINTK("mode:   %s\n", info->fix.id);
    1007             :         DPRINTK("visual: %d\n", info->fix.visual);
    1008             :         DPRINTK("res:    %dx%d-%d\n", info->var.xres,
    1009             :                 info->var.yres,
    1010             :                 info->var.bits_per_pixel);
    1011             : 
    1012           6 :         fbcon_add_cursor_timer(info);
    1013           1 :         fbcon_has_exited = 0;
    1014           1 :         return display_desc;
    1015             : }
    1016             : 
    1017             : static void fbcon_init(struct vc_data *vc, int init)
    1018             : {
    1019           3 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    1020           1 :         struct fbcon_ops *ops;
    1021           2 :         struct vc_data **default_mode = vc->vc_display_fg;
    1022           2 :         struct vc_data *svc = *default_mode;
    1023           3 :         struct display *t, *p = &fb_display[vc->vc_num];
    1024           3 :         int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256;
    1025           1 :         int cap, ret;
    1026           1 : 
    1027           5 :         if (info_idx == -1 || info == NULL)
    1028           2 :             return;
    1029           1 : 
    1030           2 :         cap = info->flags;
    1031           1 : 
    1032           7 :         if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
    1033           1 :             (info->fix.type == FB_TYPE_TEXT))
    1034           2 :                 logo = 0;
    1035           1 : 
    1036           5 :         if (var_to_display(p, &info->var, info))
    1037           2 :                 return;
    1038           1 : 
    1039           4 :         if (!info->fbcon_par)
    1040           7 :                 con2fb_acquire_newinfo(vc, info, vc->vc_num, -1);
    1041           1 : 
    1042           1 :         /* If we are not the first console on this
    1043           1 :            fb, copy the font from that console */
    1044           2 :         t = &fb_display[fg_console];
    1045           6 :         if (!p->fontdata) {
    1046           6 :                 if (t->fontdata) {
    1047           2 :                         struct vc_data *fvc = vc_cons[fg_console].d;
    1048             : 
    1049           6 :                         vc->vc_font.data = (void *)(p->fontdata =
    1050             :                                                     fvc->vc_font.data);
    1051           2 :                         vc->vc_font.width = fvc->vc_font.width;
    1052           2 :                         vc->vc_font.height = fvc->vc_font.height;
    1053           2 :                         p->userfont = t->userfont;
    1054             : 
    1055           4 :                         if (p->userfont)
    1056           2 :                                 REFCOUNT(p->fontdata)++;
    1057             :                 } else {
    1058           2 :                         const struct font_desc *font = NULL;
    1059             : 
    1060          10 :                         if (!fontname[0] || !(font = find_font(fontname)))
    1061           4 :                                 font = get_default_font(info->var.xres,
    1062             :                                                         info->var.yres,
    1063             :                                                         info->pixmap.blit_x,
    1064             :                                                         info->pixmap.blit_y);
    1065           4 :                         vc->vc_font.width = font->width;
    1066           4 :                         vc->vc_font.height = font->height;
    1067           8 :                         vc->vc_font.data = (void *)(p->fontdata = font->data);
    1068           2 :                         vc->vc_font.charcount = 256; /* FIXME  Need to
    1069             :                                                         support more fonts */
    1070             :                 }
    1071             :         }
    1072             : 
    1073           4 :         if (p->userfont)
    1074           2 :                 charcnt = FNTCHARCNT(p->fontdata);
    1075             : 
    1076           4 :         vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
    1077          12 :         vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
    1078           4 :         if (charcnt == 256) {
    1079           2 :                 vc->vc_hi_font_mask = 0;
    1080             :         } else {
    1081           2 :                 vc->vc_hi_font_mask = 0x100;
    1082           4 :                 if (vc->vc_can_do_color)
    1083           4 :                         vc->vc_complement_mask <<= 1;
    1084             :         }
    1085             : 
    1086           4 :         if (!*svc->vc_uni_pagedir_loc)
    1087           2 :                 con_set_default_unimap(svc);
    1088           4 :         if (!*vc->vc_uni_pagedir_loc)
    1089           2 :                 con_copy_unimap(vc, svc);
    1090             : 
    1091           4 :         ops = info->fbcon_par;
    1092           2 :         p->con_rotate = initial_rotation;
    1093           4 :         set_blitting_type(vc, info);
    1094             : 
    1095           2 :         cols = vc->vc_cols;
    1096           2 :         rows = vc->vc_rows;
    1097          10 :         new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
    1098          10 :         new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
    1099           1 :         new_cols /= vc->vc_font.width;
    1100           1 :         new_rows /= vc->vc_font.height;
    1101             : 
    1102             :         /*
    1103             :          * We must always set the mode. The mode of the previous console
    1104             :          * driver could be in the same resolution but we are using different
    1105             :          * hardware so we have to initialize the hardware.
    1106             :          *
    1107             :          * We need to do it in fbcon_init() to prevent screen corruption.
    1108             :          */
    1109           5 :         if (CON_IS_VISIBLE(vc) && vc->vc_mode == KD_TEXT) {
    1110           5 :                 if (info->fbops->fb_set_par &&
    1111             :                     !(ops->flags & FBCON_FLAGS_INIT)) {
    1112           1 :                         ret = info->fbops->fb_set_par(info);
    1113             : 
    1114           2 :                         if (ret)
    1115           1 :                                 printk(KERN_ERR "fbcon_init: detected "
    1116             :                                         "unhandled fb_set_par error, "
    1117             :                                         "error code %d\n", ret);
    1118             :                 }
    1119             : 
    1120           1 :                 ops->flags |= FBCON_FLAGS_INIT;
    1121             :         }
    1122             : 
    1123           1 :         ops->graphics = 0;
    1124             : 
    1125           2 :         if ((cap & FBINFO_HWACCEL_COPYAREA) &&
    1126             :             !(cap & FBINFO_HWACCEL_DISABLED))
    1127           1 :                 p->scrollmode = SCROLL_MOVE;
    1128             :         else /* default to something safe */
    1129           1 :                 p->scrollmode = SCROLL_REDRAW;
    1130             : 
    1131             :         /*
    1132             :          *  ++guenther: console.c:vc_allocate() relies on initializing
    1133             :          *  vc_{cols,rows}, but we must not set those if we are only
    1134             :          *  resizing the console.
    1135             :          */
    1136           2 :         if (init) {
    1137           1 :                 vc->vc_cols = new_cols;
    1138           1 :                 vc->vc_rows = new_rows;
    1139             :         } else
    1140           1 :                 vc_resize(vc, new_cols, new_rows);
    1141             : 
    1142           2 :         if (logo)
    1143           4 :                 fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
    1144             : 
    1145           8 :         if (vc == svc && softback_buf)
    1146           4 :                 fbcon_update_softback(vc);
    1147             : 
    1148          18 :         if (ops->rotate_font && ops->rotate_font(info, vc)) {
    1149           3 :                 ops->rotate = FB_ROTATE_UR;
    1150           6 :                 set_blitting_type(vc, info);
    1151             :         }
    1152             : 
    1153           4 :         ops->p = &fb_display[fg_console];
    1154           4 : }
    1155             : 
    1156             : static void fbcon_free_font(struct display *p)
    1157             : {
    1158           8 :         if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
    1159           1 :                 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
    1160           1 :         p->fontdata = NULL;
    1161           1 :         p->userfont = 0;
    1162           1 : }
    1163             : 
    1164             : static void fbcon_deinit(struct vc_data *vc)
    1165             : {
    1166           3 :         struct display *p = &fb_display[vc->vc_num];
    1167           1 :         struct fb_info *info;
    1168           1 :         struct fbcon_ops *ops;
    1169           1 :         int idx;
    1170           1 : 
    1171           2 :         fbcon_free_font(p);
    1172           2 :         idx = con2fb_map[vc->vc_num];
    1173             : 
    1174           2 :         if (idx == -1)
    1175           1 :                 goto finished;
    1176             : 
    1177           1 :         info = registered_fb[idx];
    1178             : 
    1179           2 :         if (!info)
    1180           1 :                 goto finished;
    1181             : 
    1182           2 :         ops = info->fbcon_par;
    1183             : 
    1184           2 :         if (!ops)
    1185           1 :                 goto finished;
    1186             : 
    1187           2 :         if (CON_IS_VISIBLE(vc))
    1188           3 :                 fbcon_del_cursor_timer(info);
    1189             : 
    1190           2 :         ops->flags &= ~FBCON_FLAGS_INIT;
    1191           2 : finished:
    1192             : 
    1193           6 :         if (!con_is_bound(&fb_con))
    1194           6 :                 fbcon_exit();
    1195             : 
    1196           3 :         return;
    1197             : }
    1198             : 
    1199             : /* ====================================================================== */
    1200             : 
    1201             : /*  fbcon_XXX routines - interface used by the world
    1202             :  *
    1203             :  *  This system is now divided into two levels because of complications
    1204             :  *  caused by hardware scrolling. Top level functions:
    1205             :  *
    1206             :  *      fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins()
    1207             :  *
    1208             :  *  handles y values in range [0, scr_height-1] that correspond to real
    1209             :  *  screen positions. y_wrap shift means that first line of bitmap may be
    1210             :  *  anywhere on this display. These functions convert lineoffsets to
    1211             :  *  bitmap offsets and deal with the wrap-around case by splitting blits.
    1212             :  *
    1213             :  *      fbcon_bmove_physical_8()    -- These functions fast implementations
    1214             :  *      fbcon_clear_physical_8()    -- of original fbcon_XXX fns.
    1215             :  *      fbcon_putc_physical_8()     -- (font width != 8) may be added later
    1216             :  *
    1217             :  *  WARNING:
    1218             :  *
    1219             :  *  At the moment fbcon_putc() cannot blit across vertical wrap boundary
    1220             :  *  Implies should only really hardware scroll in rows. Only reason for
    1221             :  *  restriction is simplicity & efficiency at the moment.
    1222             :  */
    1223             : 
    1224             : static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
    1225             :                         int width)
    1226          24 : {
    1227          72 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    1228          72 :         struct fbcon_ops *ops = info->fbcon_par;
    1229          24 : 
    1230          72 :         struct display *p = &fb_display[vc->vc_num];
    1231          24 :         u_int y_break;
    1232          24 : 
    1233         120 :         if (fbcon_is_inactive(vc, info))
    1234          48 :                 return;
    1235             : 
    1236          96 :         if (!height || !width)
    1237          24 :                 return;
    1238             : 
    1239          96 :         if (sy < vc->vc_top && vc->vc_top == logo_lines)
    1240          24 :                 vc->vc_top = 0;
    1241             : 
    1242             :         /* Split blits that cross physical y_wrap boundary */
    1243             : 
    1244          48 :         y_break = p->vrows - p->yscroll;
    1245          96 :         if (sy < y_break && sy + height - 1 >= y_break) {
    1246          24 :                 u_int b = y_break - sy;
    1247          72 :                 ops->clear(vc, info, real_y(p, sy), sx, b, width);
    1248          72 :                 ops->clear(vc, info, real_y(p, sy + b), sx, height - b,
    1249             :                                  width);
    1250             :         } else
    1251          72 :                 ops->clear(vc, info, real_y(p, sy), sx, height, width);
    1252          48 : }
    1253             : 
    1254             : static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
    1255             :                         int count, int ypos, int xpos)
    1256             : {
    1257         429 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    1258         429 :         struct display *p = &fb_display[vc->vc_num];
    1259         429 :         struct fbcon_ops *ops = info->fbcon_par;
    1260         143 : 
    1261         715 :         if (!fbcon_is_inactive(vc, info))
    1262        1430 :                 ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
    1263         143 :                            get_color(vc, info, scr_readw(s), 1),
    1264             :                            get_color(vc, info, scr_readw(s), 0));
    1265         286 : }
    1266             : 
    1267             : static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
    1268             : {
    1269           1 :         unsigned short chr;
    1270             : 
    1271           1 :         scr_writew(c, &chr);
    1272           3 :         fbcon_putcs(vc, &chr, 1, ypos, xpos);
    1273           1 : }
    1274             : 
    1275             : static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)
    1276             : {
    1277         132 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    1278         132 :         struct fbcon_ops *ops = info->fbcon_par;
    1279          44 : 
    1280         176 :         if (!fbcon_is_inactive(vc, info))
    1281          44 :                 ops->clear_margins(vc, info, bottom_only);
    1282          44 : }
    1283             : 
    1284             : static void fbcon_cursor(struct vc_data *vc, int mode)
    1285             : {
    1286         102 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    1287         102 :         struct fbcon_ops *ops = info->fbcon_par;
    1288          34 :         int y;
    1289         102 :         int c = scr_readw((u16 *) vc->vc_pos);
    1290          34 : 
    1291         238 :         if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1)
    1292          68 :                 return;
    1293             : 
    1294          68 :         if (vc->vc_cursor_type & 0x10)
    1295         102 :                 fbcon_del_cursor_timer(info);
    1296             :         else
    1297         102 :                 fbcon_add_cursor_timer(info);
    1298             : 
    1299          68 :         ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1;
    1300         136 :         if (mode & CM_SOFTBACK) {
    1301          68 :                 mode &= ~CM_SOFTBACK;
    1302          68 :                 y = softback_lines;
    1303             :         } else {
    1304         136 :                 if (softback_lines)
    1305         104 :                         fbcon_set_origin(vc);
    1306         102 :                 y = 0;
    1307             :         }
    1308             : 
    1309         442 :         ops->cursor(vc, info, mode, y, get_color(vc, info, c, 1),
    1310             :                     get_color(vc, info, c, 0));
    1311          34 :         vbl_cursor_cnt = CURSOR_DRAW_DELAY;
    1312          34 : }
    1313             : 
    1314           1 : static int scrollback_phys_max = 0;
    1315           1 : static int scrollback_max = 0;
    1316           1 : static int scrollback_current = 0;
    1317             : 
    1318             : static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
    1319             :                            int unit)
    1320             : {
    1321          16 :         struct display *p, *t;
    1322          16 :         struct vc_data **default_mode, *vc;
    1323          16 :         struct vc_data *svc;
    1324          48 :         struct fbcon_ops *ops = info->fbcon_par;
    1325          32 :         int rows, cols, charcnt = 256;
    1326          16 : 
    1327          32 :         p = &fb_display[unit];
    1328          16 : 
    1329          80 :         if (var_to_display(p, var, info))
    1330          32 :                 return;
    1331          16 : 
    1332          32 :         vc = vc_cons[unit].d;
    1333          16 : 
    1334          48 :         if (!vc)
    1335          32 :                 return;
    1336          16 : 
    1337          16 :         default_mode = vc->vc_display_fg;
    1338          16 :         svc = *default_mode;
    1339          32 :         t = &fb_display[svc->vc_num];
    1340             : 
    1341          32 :         if (!vc->vc_font.data) {
    1342          48 :                 vc->vc_font.data = (void *)(p->fontdata = t->fontdata);
    1343          16 :                 vc->vc_font.width = (*default_mode)->vc_font.width;
    1344          16 :                 vc->vc_font.height = (*default_mode)->vc_font.height;
    1345          16 :                 p->userfont = t->userfont;
    1346          32 :                 if (p->userfont)
    1347          16 :                         REFCOUNT(p->fontdata)++;
    1348             :         }
    1349          32 :         if (p->userfont)
    1350          16 :                 charcnt = FNTCHARCNT(p->fontdata);
    1351             : 
    1352          16 :         var->activate = FB_ACTIVATE_NOW;
    1353          16 :         info->var.activate = var->activate;
    1354          16 :         var->yoffset = info->var.yoffset;
    1355          16 :         var->xoffset = info->var.xoffset;
    1356          16 :         fb_set_var(info, var);
    1357          16 :         ops->var = info->var;
    1358          32 :         vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
    1359          96 :         vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
    1360          32 :         if (charcnt == 256) {
    1361          16 :                 vc->vc_hi_font_mask = 0;
    1362             :         } else {
    1363          16 :                 vc->vc_hi_font_mask = 0x100;
    1364          32 :                 if (vc->vc_can_do_color)
    1365          32 :                         vc->vc_complement_mask <<= 1;
    1366             :         }
    1367             : 
    1368          32 :         if (!*svc->vc_uni_pagedir_loc)
    1369          16 :                 con_set_default_unimap(svc);
    1370          32 :         if (!*vc->vc_uni_pagedir_loc)
    1371          16 :                 con_copy_unimap(vc, svc);
    1372             : 
    1373         160 :         cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
    1374         160 :         rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
    1375          16 :         cols /= vc->vc_font.width;
    1376          16 :         rows /= vc->vc_font.height;
    1377          16 :         vc_resize(vc, cols, rows);
    1378             : 
    1379          32 :         if (CON_IS_VISIBLE(vc)) {
    1380          16 :                 update_screen(vc);
    1381          32 :                 if (softback_buf)
    1382          32 :                         fbcon_update_softback(vc);
    1383             :         }
    1384          32 : }
    1385             : 
    1386             : static __inline__ void ywrap_up(struct vc_data *vc, int count)
    1387             : {
    1388           9 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    1389           9 :         struct fbcon_ops *ops = info->fbcon_par;
    1390           9 :         struct display *p = &fb_display[vc->vc_num];
    1391             :         
    1392           6 :         p->yscroll += count;
    1393           9 :         if (p->yscroll >= p->vrows)    /* Deal with wrap */
    1394           9 :                 p->yscroll -= p->vrows;
    1395           3 :         ops->var.xoffset = 0;
    1396           6 :         ops->var.yoffset = p->yscroll * vc->vc_font.height;
    1397           3 :         ops->var.vmode |= FB_VMODE_YWRAP;
    1398           3 :         ops->update_start(info);
    1399           3 :         scrollback_max += count;
    1400           6 :         if (scrollback_max > scrollback_phys_max)
    1401           3 :                 scrollback_max = scrollback_phys_max;
    1402           3 :         scrollback_current = 0;
    1403           3 : }
    1404             : 
    1405             : static __inline__ void ywrap_down(struct vc_data *vc, int count)
    1406             : {
    1407           6 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    1408           6 :         struct fbcon_ops *ops = info->fbcon_par;
    1409           6 :         struct display *p = &fb_display[vc->vc_num];
    1410             :         
    1411           4 :         p->yscroll -= count;
    1412           6 :         if (p->yscroll < 0)       /* Deal with wrap */
    1413           6 :                 p->yscroll += p->vrows;
    1414           2 :         ops->var.xoffset = 0;
    1415           4 :         ops->var.yoffset = p->yscroll * vc->vc_font.height;
    1416           2 :         ops->var.vmode |= FB_VMODE_YWRAP;
    1417           2 :         ops->update_start(info);
    1418           2 :         scrollback_max -= count;
    1419           4 :         if (scrollback_max < 0)
    1420           2 :                 scrollback_max = 0;
    1421           2 :         scrollback_current = 0;
    1422           2 : }
    1423             : 
    1424             : static __inline__ void ypan_up(struct vc_data *vc, int count)
    1425             : {
    1426           9 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    1427           9 :         struct display *p = &fb_display[vc->vc_num];
    1428           9 :         struct fbcon_ops *ops = info->fbcon_par;
    1429             : 
    1430           6 :         p->yscroll += count;
    1431          12 :         if (p->yscroll > p->vrows - vc->vc_rows) {
    1432          12 :                 ops->bmove(vc, info, p->vrows - vc->vc_rows,
    1433             :                             0, 0, 0, vc->vc_rows, vc->vc_cols);
    1434          12 :                 p->yscroll -= p->vrows - vc->vc_rows;
    1435             :         }
    1436             : 
    1437           3 :         ops->var.xoffset = 0;
    1438           6 :         ops->var.yoffset = p->yscroll * vc->vc_font.height;
    1439           3 :         ops->var.vmode &= ~FB_VMODE_YWRAP;
    1440           3 :         ops->update_start(info);
    1441           6 :         fbcon_clear_margins(vc, 1);
    1442           3 :         scrollback_max += count;
    1443           6 :         if (scrollback_max > scrollback_phys_max)
    1444           3 :                 scrollback_max = scrollback_phys_max;
    1445           3 :         scrollback_current = 0;
    1446           3 : }
    1447             : 
    1448             : static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
    1449             : {
    1450           9 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    1451           9 :         struct fbcon_ops *ops = info->fbcon_par;
    1452           9 :         struct display *p = &fb_display[vc->vc_num];
    1453             : 
    1454           6 :         p->yscroll += count;
    1455             : 
    1456          12 :         if (p->yscroll > p->vrows - vc->vc_rows) {
    1457          12 :                 p->yscroll -= p->vrows - vc->vc_rows;
    1458           6 :                 fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
    1459             :         }
    1460             : 
    1461           6 :         ops->var.xoffset = 0;
    1462          12 :         ops->var.yoffset = p->yscroll * vc->vc_font.height;
    1463           6 :         ops->var.vmode &= ~FB_VMODE_YWRAP;
    1464           6 :         ops->update_start(info);
    1465          12 :         fbcon_clear_margins(vc, 1);
    1466           3 :         scrollback_max += count;
    1467           6 :         if (scrollback_max > scrollback_phys_max)
    1468           3 :                 scrollback_max = scrollback_phys_max;
    1469           3 :         scrollback_current = 0;
    1470           3 : }
    1471             : 
    1472             : static __inline__ void ypan_down(struct vc_data *vc, int count)
    1473             : {
    1474           6 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    1475           6 :         struct display *p = &fb_display[vc->vc_num];
    1476           6 :         struct fbcon_ops *ops = info->fbcon_par;
    1477             :         
    1478           4 :         p->yscroll -= count;
    1479           6 :         if (p->yscroll < 0) {
    1480           8 :                 ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
    1481             :                             0, vc->vc_rows, vc->vc_cols);
    1482           8 :                 p->yscroll += p->vrows - vc->vc_rows;
    1483             :         }
    1484             : 
    1485           2 :         ops->var.xoffset = 0;
    1486           4 :         ops->var.yoffset = p->yscroll * vc->vc_font.height;
    1487           2 :         ops->var.vmode &= ~FB_VMODE_YWRAP;
    1488           2 :         ops->update_start(info);
    1489           4 :         fbcon_clear_margins(vc, 1);
    1490           2 :         scrollback_max -= count;
    1491           4 :         if (scrollback_max < 0)
    1492           2 :                 scrollback_max = 0;
    1493           2 :         scrollback_current = 0;
    1494           2 : }
    1495             : 
    1496             : static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
    1497             : {
    1498           6 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    1499           6 :         struct fbcon_ops *ops = info->fbcon_par;
    1500           6 :         struct display *p = &fb_display[vc->vc_num];
    1501             : 
    1502           4 :         p->yscroll -= count;
    1503             : 
    1504           6 :         if (p->yscroll < 0) {
    1505           8 :                 p->yscroll += p->vrows - vc->vc_rows;
    1506           4 :                 fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
    1507             :         }
    1508             : 
    1509           4 :         ops->var.xoffset = 0;
    1510           8 :         ops->var.yoffset = p->yscroll * vc->vc_font.height;
    1511           4 :         ops->var.vmode &= ~FB_VMODE_YWRAP;
    1512           4 :         ops->update_start(info);
    1513           8 :         fbcon_clear_margins(vc, 1);
    1514           2 :         scrollback_max -= count;
    1515           4 :         if (scrollback_max < 0)
    1516           2 :                 scrollback_max = 0;
    1517           2 :         scrollback_current = 0;
    1518           2 : }
    1519             : 
    1520             : static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
    1521             :                                   long delta)
    1522          14 : {
    1523          42 :         int count = vc->vc_rows;
    1524          14 :         unsigned short *d, *s;
    1525          14 :         unsigned long n;
    1526          28 :         int line = 0;
    1527          14 : 
    1528          28 :         d = (u16 *) softback_curr;
    1529          42 :         if (d == (u16 *) softback_in)
    1530          42 :                 d = (u16 *) vc->vc_origin;
    1531          42 :         n = softback_curr + delta * vc->vc_size_row;
    1532          28 :         softback_lines -= delta;
    1533          28 :         if (delta < 0) {
    1534          56 :                 if (softback_curr < softback_top && n < softback_buf) {
    1535          14 :                         n += softback_end - softback_buf;
    1536          28 :                         if (n < softback_top) {
    1537          28 :                                 softback_lines -=
    1538             :                                     (softback_top - n) / vc->vc_size_row;
    1539          14 :                                 n = softback_top;
    1540             :                         }
    1541          56 :                 } else if (softback_curr >= softback_top
    1542             :                            && n < softback_top) {
    1543          28 :                         softback_lines -=
    1544             :                             (softback_top - n) / vc->vc_size_row;
    1545          14 :                         n = softback_top;
    1546             :                 }
    1547             :         } else {
    1548          56 :                 if (softback_curr > softback_in && n >= softback_end) {
    1549          14 :                         n += softback_buf - softback_end;
    1550          28 :                         if (n > softback_in) {
    1551          14 :                                 n = softback_in;
    1552          14 :                                 softback_lines = 0;
    1553             :                         }
    1554          56 :                 } else if (softback_curr <= softback_in && n > softback_in) {
    1555          14 :                         n = softback_in;
    1556          14 :                         softback_lines = 0;
    1557             :                 }
    1558             :         }
    1559          28 :         if (n == softback_curr)
    1560          14 :                 return;
    1561          14 :         softback_curr = n;
    1562          14 :         s = (u16 *) softback_curr;
    1563          28 :         if (s == (u16 *) softback_in)
    1564          28 :                 s = (u16 *) vc->vc_origin;
    1565          70 :         while (count--) {
    1566          14 :                 unsigned short *start;
    1567          14 :                 unsigned short *le;
    1568             :                 unsigned short c;
    1569          14 :                 int x = 0;
    1570          14 :                 unsigned short attr = 1;
    1571             : 
    1572          28 :                 start = s;
    1573          28 :                 le = advance_row(s, 1);
    1574          14 :                 do {
    1575          14 :                         c = scr_readw(s);
    1576          28 :                         if (attr != (c & 0xff00)) {
    1577          14 :                                 attr = c & 0xff00;
    1578          28 :                                 if (s > start) {
    1579          42 :                                         fbcon_putcs(vc, start, s - start,
    1580             :                                                     line, x);
    1581          14 :                                         x += s - start;
    1582          14 :                                         start = s;
    1583             :                                 }
    1584             :                         }
    1585          56 :                         if (c == scr_readw(d)) {
    1586          56 :                                 if (s > start) {
    1587          84 :                                         fbcon_putcs(vc, start, s - start,
    1588             :                                                     line, x);
    1589          14 :                                         x += s - start + 1;
    1590          14 :                                         start = s + 1;
    1591             :                                 } else {
    1592          28 :                                         x++;
    1593          28 :                                         start++;
    1594             :                                 }
    1595             :                         }
    1596          42 :                         s++;
    1597          42 :                         d++;
    1598          84 :                 } while (s < le);
    1599          84 :                 if (s > start)
    1600         168 :                         fbcon_putcs(vc, start, s - start, line, x);
    1601          56 :                 line++;
    1602         112 :                 if (d == (u16 *) softback_end)
    1603          56 :                         d = (u16 *) softback_buf;
    1604         112 :                 if (d == (u16 *) softback_in)
    1605         112 :                         d = (u16 *) vc->vc_origin;
    1606         112 :                 if (s == (u16 *) softback_end)
    1607          56 :                         s = (u16 *) softback_buf;
    1608         112 :                 if (s == (u16 *) softback_in)
    1609         112 :                         s = (u16 *) vc->vc_origin;
    1610             :         }
    1611             : }
    1612             : 
    1613          56 : static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
    1614             :                               int line, int count, int dy)
    1615             : {
    1616          26 :         unsigned short *s = (unsigned short *)
    1617          13 :                 (vc->vc_origin + vc->vc_size_row * line);
    1618          13 : 
    1619          78 :         while (count--) {
    1620          39 :                 unsigned short *start = s;
    1621          52 :                 unsigned short *le = advance_row(s, 1);
    1622          13 :                 unsigned short c;
    1623          13 :                 int x = 0;
    1624          13 :                 unsigned short attr = 1;
    1625          13 : 
    1626          13 :                 do {
    1627          13 :                         c = scr_readw(s);
    1628          26 :                         if (attr != (c & 0xff00)) {
    1629          13 :                                 attr = c & 0xff00;
    1630          26 :                                 if (s > start) {
    1631          39 :                                         fbcon_putcs(vc, start, s - start,
    1632             :                                                     dy, x);
    1633          13 :                                         x += s - start;
    1634          13 :                                         start = s;
    1635             :                                 }
    1636             :                         }
    1637          26 :                         console_conditional_schedule();
    1638          26 :                         s++;
    1639          52 :                 } while (s < le);
    1640          52 :                 if (s > start)
    1641         104 :                         fbcon_putcs(vc, start, s - start, dy, x);
    1642          39 :                 console_conditional_schedule();
    1643          39 :                 dy++;
    1644             :         }
    1645          39 : }
    1646             : 
    1647             : static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info,
    1648             :                         struct display *p, int line, int count, int ycount)
    1649             : {
    1650           6 :         int offset = ycount * vc->vc_cols;
    1651           6 :         unsigned short *d = (unsigned short *)
    1652           3 :             (vc->vc_origin + vc->vc_size_row * line);
    1653           6 :         unsigned short *s = d + offset;
    1654           9 :         struct fbcon_ops *ops = info->fbcon_par;
    1655           3 : 
    1656          18 :         while (count--) {
    1657           9 :                 unsigned short *start = s;
    1658          12 :                 unsigned short *le = advance_row(s, 1);
    1659             :                 unsigned short c;
    1660           3 :                 int x = 0;
    1661           3 : 
    1662             :                 do {
    1663           6 :                         c = scr_readw(s);
    1664             : 
    1665           6 :                         if (c == scr_readw(d)) {
    1666           6 :                                 if (s > start) {
    1667           3 :                                         ops->bmove(vc, info, line + ycount, x,
    1668             :                                                    line, x, 1, s-start);
    1669           3 :                                         x += s - start + 1;
    1670           3 :                                         start = s + 1;
    1671             :                                 } else {
    1672           3 :                                         x++;
    1673           3 :                                         start++;
    1674             :                                 }
    1675             :                         }
    1676             : 
    1677           3 :                         scr_writew(c, d);
    1678           3 :                         console_conditional_schedule();
    1679           3 :                         s++;
    1680           3 :                         d++;
    1681           6 :                 } while (s < le);
    1682           6 :                 if (s > start)
    1683           6 :                         ops->bmove(vc, info, line + ycount, x, line, x, 1,
    1684             :                                    s-start);
    1685           3 :                 console_conditional_schedule();
    1686           6 :                 if (ycount > 0)
    1687           3 :                         line++;
    1688             :                 else {
    1689           3 :                         line--;
    1690             :                         /* NOTE: We subtract two lines from these pointers */
    1691           6 :                         s -= vc->vc_size_row;
    1692           6 :                         d -= vc->vc_size_row;
    1693             :                 }
    1694           3 :         }
    1695             : }
    1696             : 
    1697             : static void fbcon_redraw(struct vc_data *vc, struct display *p,
    1698             :                          int line, int count, int offset)
    1699             : {
    1700           6 :         unsigned short *d = (unsigned short *)
    1701           3 :             (vc->vc_origin + vc->vc_size_row * line);
    1702           6 :         unsigned short *s = d + offset;
    1703           3 : 
    1704          18 :         while (count--) {
    1705           9 :                 unsigned short *start = s;
    1706          12 :                 unsigned short *le = advance_row(s, 1);
    1707           3 :                 unsigned short c;
    1708           3 :                 int x = 0;
    1709           3 :                 unsigned short attr = 1;
    1710           3 : 
    1711           3 :                 do {
    1712           3 :                         c = scr_readw(s);
    1713           6 :                         if (attr != (c & 0xff00)) {
    1714           3 :                                 attr = c & 0xff00;
    1715           6 :                                 if (s > start) {
    1716           9 :                                         fbcon_putcs(vc, start, s - start,
    1717             :                                                     line, x);
    1718           3 :                                         x += s - start;
    1719           3 :                                         start = s;
    1720             :                                 }
    1721             :                         }
    1722          12 :                         if (c == scr_readw(d)) {
    1723          12 :                                 if (s > start) {
    1724          18 :                                         fbcon_putcs(vc, start, s - start,
    1725             :                                                      line, x);
    1726           3 :                                         x += s - start + 1;
    1727           3 :                                         start = s + 1;
    1728             :                                 } else {
    1729           6 :                                         x++;
    1730           6 :                                         start++;
    1731             :                                 }
    1732             :                         }
    1733           9 :                         scr_writew(c, d);
    1734           9 :                         console_conditional_schedule();
    1735           9 :                         s++;
    1736           9 :                         d++;
    1737          18 :                 } while (s < le);
    1738          18 :                 if (s > start)
    1739          36 :                         fbcon_putcs(vc, start, s - start, line, x);
    1740          12 :                 console_conditional_schedule();
    1741          24 :                 if (offset > 0)
    1742          12 :                         line++;
    1743             :                 else {
    1744          12 :                         line--;
    1745             :                         /* NOTE: We subtract two lines from these pointers */
    1746          24 :                         s -= vc->vc_size_row;
    1747          24 :                         d -= vc->vc_size_row;
    1748             :                 }
    1749          12 :         }
    1750             : }
    1751             : 
    1752             : static inline void fbcon_softback_note(struct vc_data *vc, int t,
    1753             :                                        int count)
    1754           1 : {
    1755             :         unsigned short *p;
    1756             : 
    1757           3 :         if (vc->vc_num != fg_console)
    1758           1 :                 return;
    1759           1 :         p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
    1760             : 
    1761           3 :         while (count) {
    1762           3 :                 scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row);
    1763           2 :                 count--;
    1764           2 :                 p = advance_row(p, 1);
    1765           2 :                 softback_in += vc->vc_size_row;
    1766           2 :                 if (softback_in == softback_end)
    1767           1 :                         softback_in = softback_buf;
    1768           2 :                 if (softback_in == softback_top) {
    1769           2 :                         softback_top += vc->vc_size_row;
    1770           2 :                         if (softback_top == softback_end)
    1771           1 :                                 softback_top = softback_buf;
    1772             :                 }
    1773             :         }
    1774           1 :         softback_curr = softback_in;
    1775           1 : }
    1776             : 
    1777             : static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
    1778           1 :                         int count)
    1779           1 : {
    1780           3 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    1781           3 :         struct display *p = &fb_display[vc->vc_num];
    1782           2 :         int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
    1783             : 
    1784           4 :         if (fbcon_is_inactive(vc, info))
    1785           1 :                 return -EINVAL;
    1786             : 
    1787           3 :         fbcon_cursor(vc, CM_ERASE);
    1788             : 
    1789             :         /*
    1790             :          * ++Geert: Only use ywrap/ypan if the console is in text mode
    1791             :          * ++Andrew: Only use ypan on hardware text mode when scrolling the
    1792             :          *           whole screen (prevents flicker).
    1793             :          */
    1794             : 
    1795           1 :         switch (dir) {
    1796           4 :         case SM_UP:
    1797           2 :                 if (count > vc->vc_rows)  /* Maximum realistic size */
    1798           2 :                         count = vc->vc_rows;
    1799           2 :                 if (softback_top)
    1800           3 :                         fbcon_softback_note(vc, t, count);
    1801           4 :                 if (logo_shown >= 0)
    1802           2 :                         goto redraw_up;
    1803           2 :                 switch (p->scrollmode) {
    1804          10 :                 case SCROLL_MOVE:
    1805           4 :                         fbcon_redraw_blit(vc, info, p, t, b - t - count,
    1806             :                                      count);
    1807           5 :                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
    1808           3 :                         scr_memsetw((unsigned short *) (vc->vc_origin +
    1809             :                                                         vc->vc_size_row *
    1810             :                                                         (b - count)),
    1811             :                                     vc->vc_video_erase_char,
    1812             :                                     vc->vc_size_row * count);
    1813           1 :                         return 1;
    1814           2 :                         break;
    1815             : 
    1816           8 :                 case SCROLL_WRAP_MOVE:
    1817           4 :                         if (b - t - count > 3 * vc->vc_rows >> 2) {
    1818           4 :                                 if (t > 0)
    1819           8 :                                         fbcon_bmove(vc, 0, 0, count, 0, t,
    1820             :                                                     vc->vc_cols);
    1821           6 :                                 ywrap_up(vc, count);
    1822           2 :                                 if (vc->vc_rows - b > 0)
    1823           4 :                                         fbcon_bmove(vc, b - count, 0, b, 0,
    1824             :                                                     vc->vc_rows - b,
    1825             :                                                     vc->vc_cols);
    1826           4 :                         } else if (info->flags & FBINFO_READS_FAST)
    1827           8 :                                 fbcon_bmove(vc, t + count, 0, t, 0,
    1828             :                                             b - t - count, vc->vc_cols);
    1829             :                         else
    1830           2 :                                 goto redraw_up;
    1831          15 :                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
    1832           1 :                         break;
    1833           2 : 
    1834           8 :                 case SCROLL_PAN_REDRAW:
    1835          24 :                         if ((p->yscroll + count <=
    1836             :                              2 * (p->vrows - vc->vc_rows))
    1837             :                             && ((!scroll_partial && (b - t == vc->vc_rows))
    1838             :                                 || (scroll_partial
    1839             :                                     && (b - t - count >
    1840             :                                         3 * vc->vc_rows >> 2)))) {
    1841           4 :                                 if (t > 0)
    1842           4 :                                         fbcon_redraw_move(vc, p, 0, t, count);
    1843           6 :                                 ypan_up_redraw(vc, t, count);
    1844           2 :                                 if (vc->vc_rows - b > 0)
    1845           2 :                                         fbcon_redraw_move(vc, p, b,
    1846             :                                                           vc->vc_rows - b, b);
    1847             :                         } else
    1848           4 :                                 fbcon_redraw_move(vc, p, t + count, b - t - count, t);
    1849          15 :                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
    1850           1 :                         break;
    1851           2 : 
    1852           8 :                 case SCROLL_PAN_MOVE:
    1853          24 :                         if ((p->yscroll + count <=
    1854             :                              2 * (p->vrows - vc->vc_rows))
    1855             :                             && ((!scroll_partial && (b - t == vc->vc_rows))
    1856             :                                 || (scroll_partial
    1857             :                                     && (b - t - count >
    1858             :                                         3 * vc->vc_rows >> 2)))) {
    1859           4 :                                 if (t > 0)
    1860           8 :                                         fbcon_bmove(vc, 0, 0, count, 0, t,
    1861             :                                                     vc->vc_cols);
    1862           6 :                                 ypan_up(vc, count);
    1863           2 :                                 if (vc->vc_rows - b > 0)
    1864           4 :                                         fbcon_bmove(vc, b - count, 0, b, 0,
    1865             :                                                     vc->vc_rows - b,
    1866             :                                                     vc->vc_cols);
    1867           4 :                         } else if (info->flags & FBINFO_READS_FAST)
    1868           8 :                                 fbcon_bmove(vc, t + count, 0, t, 0,
    1869             :                                             b - t - count, vc->vc_cols);
    1870             :                         else
    1871           2 :                                 goto redraw_up;
    1872          15 :                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
    1873           1 :                         break;
    1874           2 : 
    1875          10 :                 case SCROLL_REDRAW:
    1876             :                       redraw_up:
    1877           4 :                         fbcon_redraw(vc, p, t, b - t - count,
    1878             :                                      count * vc->vc_cols);
    1879           5 :                         fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
    1880           3 :                         scr_memsetw((unsigned short *) (vc->vc_origin +
    1881             :                                                         vc->vc_size_row *
    1882             :                                                         (b - count)),
    1883             :                                     vc->vc_video_erase_char,
    1884             :                                     vc->vc_size_row * count);
    1885           1 :                         return 1;
    1886           2 :                 }
    1887           5 :                 break;
    1888           3 : 
    1889           3 :         case SM_DOWN:
    1890           2 :                 if (count > vc->vc_rows)  /* Maximum realistic size */
    1891           2 :                         count = vc->vc_rows;
    1892           2 :                 if (logo_shown >= 0)
    1893           1 :                         goto redraw_down;
    1894           1 :                 switch (p->scrollmode) {
    1895           5 :                 case SCROLL_MOVE:
    1896           2 :                         fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
    1897             :                                      -count);
    1898           5 :                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
    1899           3 :                         scr_memsetw((unsigned short *) (vc->vc_origin +
    1900             :                                                         vc->vc_size_row *
    1901             :                                                         t),
    1902             :                                     vc->vc_video_erase_char,
    1903             :                                     vc->vc_size_row * count);
    1904           1 :                         return 1;
    1905           1 :                         break;
    1906             : 
    1907           4 :                 case SCROLL_WRAP_MOVE:
    1908           2 :                         if (b - t - count > 3 * vc->vc_rows >> 2) {
    1909           2 :                                 if (vc->vc_rows - b > 0)
    1910           4 :                                         fbcon_bmove(vc, b, 0, b - count, 0,
    1911             :                                                     vc->vc_rows - b,
    1912             :                                                     vc->vc_cols);
    1913           4 :                                 ywrap_down(vc, count);
    1914           2 :                                 if (t > 0)
    1915           4 :                                         fbcon_bmove(vc, count, 0, 0, 0, t,
    1916             :                                                     vc->vc_cols);
    1917           2 :                         } else if (info->flags & FBINFO_READS_FAST)
    1918           4 :                                 fbcon_bmove(vc, t, 0, t + count, 0,
    1919             :                                             b - t - count, vc->vc_cols);
    1920             :                         else
    1921           1 :                                 goto redraw_down;
    1922          15 :                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
    1923           1 :                         break;
    1924           1 : 
    1925           4 :                 case SCROLL_PAN_MOVE:
    1926          12 :                         if ((count - p->yscroll <= p->vrows - vc->vc_rows)
    1927             :                             && ((!scroll_partial && (b - t == vc->vc_rows))
    1928             :                                 || (scroll_partial
    1929             :                                     && (b - t - count >
    1930             :                                         3 * vc->vc_rows >> 2)))) {
    1931           2 :                                 if (vc->vc_rows - b > 0)
    1932           4 :                                         fbcon_bmove(vc, b, 0, b - count, 0,
    1933             :                                                     vc->vc_rows - b,
    1934             :                                                     vc->vc_cols);
    1935           4 :                                 ypan_down(vc, count);
    1936           2 :                                 if (t > 0)
    1937           4 :                                         fbcon_bmove(vc, count, 0, 0, 0, t,
    1938             :                                                     vc->vc_cols);
    1939           2 :                         } else if (info->flags & FBINFO_READS_FAST)
    1940           4 :                                 fbcon_bmove(vc, t, 0, t + count, 0,
    1941             :                                             b - t - count, vc->vc_cols);
    1942             :                         else
    1943           1 :                                 goto redraw_down;
    1944          15 :                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
    1945           1 :                         break;
    1946           1 : 
    1947           4 :                 case SCROLL_PAN_REDRAW:
    1948          12 :                         if ((count - p->yscroll <= p->vrows - vc->vc_rows)
    1949             :                             && ((!scroll_partial && (b - t == vc->vc_rows))
    1950             :                                 || (scroll_partial
    1951             :                                     && (b - t - count >
    1952             :                                         3 * vc->vc_rows >> 2)))) {
    1953           2 :                                 if (vc->vc_rows - b > 0)
    1954           2 :                                         fbcon_redraw_move(vc, p, b, vc->vc_rows - b,
    1955             :                                                           b - count);
    1956           4 :                                 ypan_down_redraw(vc, t, count);
    1957           2 :                                 if (t > 0)
    1958           2 :                                         fbcon_redraw_move(vc, p, count, t, 0);
    1959             :                         } else
    1960           2 :                                 fbcon_redraw_move(vc, p, t, b - t - count, t + count);
    1961          15 :                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
    1962           1 :                         break;
    1963           1 : 
    1964           5 :                 case SCROLL_REDRAW:
    1965             :                       redraw_down:
    1966           2 :                         fbcon_redraw(vc, p, b - 1, b - t - count,
    1967             :                                      -count * vc->vc_cols);
    1968           5 :                         fbcon_clear(vc, t, 0, count, vc->vc_cols);
    1969           3 :                         scr_memsetw((unsigned short *) (vc->vc_origin +
    1970             :                                                         vc->vc_size_row *
    1971             :                                                         t),
    1972             :                                     vc->vc_video_erase_char,
    1973             :                                     vc->vc_size_row * count);
    1974           1 :                         return 1;
    1975           1 :                 }
    1976             :         }
    1977           9 :         return 0;
    1978           4 : }
    1979             : 
    1980           4 : 
    1981             : static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
    1982             :                         int height, int width)
    1983             : {
    1984          51 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    1985          51 :         struct display *p = &fb_display[vc->vc_num];
    1986          17 :         
    1987          68 :         if (fbcon_is_inactive(vc, info))
    1988          17 :                 return;
    1989             : 
    1990          68 :         if (!width || !height)
    1991          17 :                 return;
    1992             : 
    1993             :         /*  Split blits that cross physical y_wrap case.
    1994             :          *  Pathological case involves 4 blits, better to use recursive
    1995             :          *  code rather than unrolled case
    1996             :          *
    1997             :          *  Recursive invocations don't need to erase the cursor over and
    1998             :          *  over again, so we use fbcon_bmove_rec()
    1999             :          */
    2000         119 :         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
    2001             :                         p->vrows - p->yscroll);
    2002          17 : }
    2003             : 
    2004             : static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx, 
    2005             :                             int dy, int dx, int height, int width, u_int y_break)
    2006             : {
    2007          51 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    2008          51 :         struct fbcon_ops *ops = info->fbcon_par;
    2009          17 :         u_int b;
    2010          17 : 
    2011          85 :         if (sy < y_break && sy + height > y_break) {
    2012          17 :                 b = y_break - sy;
    2013          34 :                 if (dy < sy) {       /* Avoid trashing self */
    2014          17 :                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
    2015             :                                         y_break);
    2016          17 :                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
    2017             :                                         height - b, width, y_break);
    2018             :                 } else {
    2019          17 :                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
    2020             :                                         height - b, width, y_break);
    2021          17 :                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
    2022             :                                         y_break);
    2023             :                 }
    2024          34 :                 return;
    2025             :         }
    2026             : 
    2027          68 :         if (dy < y_break && dy + height > y_break) {
    2028          17 :                 b = y_break - dy;
    2029          34 :                 if (dy < sy) {       /* Avoid trashing self */
    2030          17 :                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
    2031             :                                         y_break);
    2032          17 :                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
    2033             :                                         height - b, width, y_break);
    2034             :                 } else {
    2035          17 :                         fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
    2036             :                                         height - b, width, y_break);
    2037          17 :                         fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
    2038             :                                         y_break);
    2039             :                 }
    2040          34 :                 return;
    2041             :         }
    2042          85 :         ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
    2043          17 :                    height, width);
    2044             : }
    2045             : 
    2046             : static void updatescrollmode(struct display *p,
    2047             :                                         struct fb_info *info,
    2048           6 :                                         struct vc_data *vc)
    2049           6 : {
    2050          18 :         struct fbcon_ops *ops = info->fbcon_par;
    2051          12 :         int fh = vc->vc_font.height;
    2052          12 :         int cap = info->flags;
    2053          12 :         u16 t = 0;
    2054          66 :         int ypan = FBCON_SWAP(ops->rotate, info->fix.ypanstep,
    2055           6 :                                   info->fix.xpanstep);
    2056          66 :         int ywrap = FBCON_SWAP(ops->rotate, info->fix.ywrapstep, t);
    2057          66 :         int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
    2058          66 :         int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
    2059           6 :                                    info->var.xres_virtual);
    2060          66 :         int good_pan = (cap & FBINFO_HWACCEL_YPAN) &&
    2061           6 :                 divides(ypan, vc->vc_font.height) && vyres > yres;
    2062         102 :         int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) &&
    2063           6 :                 divides(ywrap, vc->vc_font.height) &&
    2064           6 :                 divides(vc->vc_font.height, vyres) &&
    2065           6 :                 divides(vc->vc_font.height, yres);
    2066          12 :         int reading_fast = cap & FBINFO_READS_FAST;
    2067          12 :         int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) &&
    2068           6 :                 !(cap & FBINFO_HWACCEL_DISABLED);
    2069           6 :         int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) &&
    2070             :                 !(cap & FBINFO_HWACCEL_DISABLED);
    2071             : 
    2072           6 :         p->vrows = vyres/fh;
    2073          12 :         if (yres > (fh * (vc->vc_rows + 1)))
    2074          12 :                 p->vrows -= (yres - (fh * vc->vc_rows)) / fh;
    2075          24 :         if ((yres % fh) && (vyres % fh < yres % fh))
    2076           6 :                 p->vrows--;
    2077             : 
    2078          12 :         if (good_wrap || good_pan) {
    2079          12 :                 if (reading_fast || fast_copyarea)
    2080          36 :                         p->scrollmode = good_wrap ?
    2081             :                                 SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
    2082             :                 else
    2083          36 :                         p->scrollmode = good_wrap ? SCROLL_REDRAW :
    2084             :                                 SCROLL_PAN_REDRAW;
    2085             :         } else {
    2086          36 :                 if (reading_fast || (fast_copyarea && !fast_imageblit))
    2087           6 :                         p->scrollmode = SCROLL_MOVE;
    2088             :                 else
    2089           6 :                         p->scrollmode = SCROLL_REDRAW;
    2090           6 :         }
    2091             : }
    2092             : 
    2093             : static int fbcon_resize(struct vc_data *vc, unsigned int width, 
    2094             :                         unsigned int height, unsigned int user)
    2095             : {
    2096           3 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    2097           3 :         struct fbcon_ops *ops = info->fbcon_par;
    2098           3 :         struct display *p = &fb_display[vc->vc_num];
    2099           2 :         struct fb_var_screeninfo var = info->var;
    2100           1 :         int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
    2101           1 : 
    2102          11 :         virt_w = FBCON_SWAP(ops->rotate, width, height);
    2103          11 :         virt_h = FBCON_SWAP(ops->rotate, height, width);
    2104          11 :         virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width,
    2105           1 :                                  vc->vc_font.height);
    2106          11 :         virt_fh = FBCON_SWAP(ops->rotate, vc->vc_font.height,
    2107           1 :                                  vc->vc_font.width);
    2108           2 :         var.xres = virt_w * virt_fw;
    2109           2 :         var.yres = virt_h * virt_fh;
    2110           2 :         x_diff = info->var.xres - var.xres;
    2111           2 :         y_diff = info->var.yres - var.yres;
    2112           9 :         if (x_diff < 0 || x_diff > virt_fw ||
    2113           1 :             y_diff < 0 || y_diff > virt_fh) {
    2114           1 :                 const struct fb_videomode *mode;
    2115             : 
    2116             :                 DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
    2117           1 :                 mode = fb_find_best_mode(&var, &info->modelist);
    2118           2 :                 if (mode == NULL)
    2119           1 :                         return -EINVAL;
    2120           2 :                 display_to_var(&var, p);
    2121           1 :                 fb_videomode_to_var(&var, mode);
    2122             : 
    2123           4 :                 if (virt_w > var.xres/virt_fw || virt_h > var.yres/virt_fh)
    2124           1 :                         return -EINVAL;
    2125             : 
    2126             :                 DPRINTK("resize now %ix%i\n", var.xres, var.yres);
    2127           2 :                 if (CON_IS_VISIBLE(vc)) {
    2128           1 :                         var.activate = FB_ACTIVATE_NOW |
    2129             :                                 FB_ACTIVATE_FORCE;
    2130           1 :                         fb_set_var(info, &var);
    2131             :                 }
    2132           2 :                 var_to_display(p, &info->var, info);
    2133           1 :                 ops->var = info->var;
    2134             :         }
    2135           4 :         updatescrollmode(p, info, vc);
    2136           1 :         return 0;
    2137             : }
    2138             : 
    2139             : static int fbcon_switch(struct vc_data *vc)
    2140             : {
    2141           2 :         struct fb_info *info, *old_info = NULL;
    2142           1 :         struct fbcon_ops *ops;
    2143           3 :         struct display *p = &fb_display[vc->vc_num];
    2144           1 :         struct fb_var_screeninfo var;
    2145           2 :         int i, ret, prev_console, charcnt = 256;
    2146           1 : 
    2147           3 :         info = registered_fb[con2fb_map[vc->vc_num]];
    2148           3 :         ops = info->fbcon_par;
    2149           1 : 
    2150           3 :         if (softback_top) {
    2151           3 :                 if (softback_lines)
    2152           4 :                         fbcon_set_origin(vc);
    2153           7 :                 softback_top = softback_curr = softback_in = softback_buf;
    2154           3 :                 softback_lines = 0;
    2155           5 :                 fbcon_update_softback(vc);
    2156           1 :         }
    2157             : 
    2158           4 :         if (logo_shown >= 0) {
    2159           2 :                 struct vc_data *conp2 = vc_cons[logo_shown].d;
    2160             : 
    2161           8 :                 if (conp2->vc_top == logo_lines
    2162             :                     && conp2->vc_bottom == conp2->vc_rows)
    2163           2 :                         conp2->vc_top = 0;
    2164           2 :                 logo_shown = FBCON_LOGO_CANSHOW;
    2165             :         }
    2166             : 
    2167           2 :         prev_console = ops->currcon;
    2168           4 :         if (prev_console != -1)
    2169           2 :                 old_info = registered_fb[con2fb_map[prev_console]];
    2170             :         /*
    2171             :          * FIXME: If we have multiple fbdev's loaded, we need to
    2172             :          * update all info->currcon.  Perhaps, we can place this
    2173             :          * in a centralized structure, but this might break some
    2174             :          * drivers.
    2175             :          *
    2176             :          * info->currcon = vc->vc_num;
    2177             :          */
    2178           7 :         for (i = 0; i < FB_MAX; i++) {
    2179           7 :                 if (registered_fb[i] != NULL && registered_fb[i]->fbcon_par) {
    2180           3 :                         struct fbcon_ops *o = registered_fb[i]->fbcon_par;
    2181             : 
    2182           2 :                         o->currcon = vc->vc_num;
    2183             :                 }
    2184             :         }
    2185           1 :         memset(&var, 0, sizeof(struct fb_var_screeninfo));
    2186           2 :         display_to_var(&var, p);
    2187           1 :         var.activate = FB_ACTIVATE_NOW;
    2188             : 
    2189             :         /*
    2190             :          * make sure we don't unnecessarily trip the memcmp()
    2191             :          * in fb_set_var()
    2192             :          */
    2193           1 :         info->var.activate = var.activate;
    2194           1 :         var.vmode |= info->var.vmode & ~FB_VMODE_MASK;
    2195           1 :         fb_set_var(info, &var);
    2196           1 :         ops->var = info->var;
    2197             : 
    2198           6 :         if (old_info != NULL && (old_info != info ||
    2199             :                                  info->flags & FBINFO_MISC_ALWAYS_SETPAR)) {
    2200           3 :                 if (info->fbops->fb_set_par) {
    2201           1 :                         ret = info->fbops->fb_set_par(info);
    2202             : 
    2203           2 :                         if (ret)
    2204           1 :                                 printk(KERN_ERR "fbcon_switch: detected "
    2205             :                                         "unhandled fb_set_par error, "
    2206             :                                         "error code %d\n", ret);
    2207             :                 }
    2208             : 
    2209           2 :                 if (old_info != info)
    2210           3 :                         fbcon_del_cursor_timer(old_info);
    2211             :         }
    2212             : 
    2213           8 :         if (fbcon_is_inactive(vc, info) ||
    2214             :             ops->blank_state != FB_BLANK_UNBLANK)
    2215           3 :                 fbcon_del_cursor_timer(info);
    2216             :         else
    2217           3 :                 fbcon_add_cursor_timer(info);
    2218             : 
    2219           4 :         set_blitting_type(vc, info);
    2220           1 :         ops->cursor_reset = 1;
    2221             : 
    2222           6 :         if (ops->rotate_font && ops->rotate_font(info, vc)) {
    2223           1 :                 ops->rotate = FB_ROTATE_UR;
    2224           2 :                 set_blitting_type(vc, info);
    2225             :         }
    2226             : 
    2227           4 :         vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
    2228          12 :         vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
    2229             : 
    2230           4 :         if (p->userfont)
    2231           2 :                 charcnt = FNTCHARCNT(vc->vc_font.data);
    2232             : 
    2233           4 :         if (charcnt > 256)
    2234           4 :                 vc->vc_complement_mask <<= 1;
    2235             : 
    2236           4 :         updatescrollmode(p, info, vc);
    2237             : 
    2238             :         switch (p->scrollmode) {
    2239           4 :         case SCROLL_WRAP_MOVE:
    2240           2 :                 scrollback_phys_max = p->vrows - vc->vc_rows;
    2241           1 :                 break;
    2242           5 :         case SCROLL_PAN_MOVE:
    2243           5 :         case SCROLL_PAN_REDRAW:
    2244           2 :                 scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
    2245           2 :                 if (scrollback_phys_max < 0)
    2246           1 :                         scrollback_phys_max = 0;
    2247           1 :                 break;
    2248           2 :         default:
    2249           2 :                 scrollback_phys_max = 0;
    2250           1 :                 break;
    2251             :         }
    2252             : 
    2253           1 :         scrollback_max = 0;
    2254           1 :         scrollback_current = 0;
    2255             : 
    2256           4 :         if (!fbcon_is_inactive(vc, info)) {
    2257           4 :             ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
    2258           1 :             ops->update_start(info);
    2259             :         }
    2260             : 
    2261           3 :         fbcon_set_palette(vc, color_table);     
    2262           2 :         fbcon_clear_margins(vc, 0);
    2263             : 
    2264           2 :         if (logo_shown == FBCON_LOGO_DRAW) {
    2265             : 
    2266           1 :                 logo_shown = fg_console;
    2267             :                 /* This is protected above by initmem_freed */
    2268           1 :                 fb_show_logo(info, ops->rotate);
    2269           1 :                 update_region(vc,
    2270             :                               vc->vc_origin + vc->vc_size_row * vc->vc_top,
    2271             :                               vc->vc_size_row * (vc->vc_bottom -
    2272             :                                                  vc->vc_top) / 2);
    2273           1 :                 return 0;
    2274             :         }
    2275           1 :         return 1;
    2276             : }
    2277             : 
    2278             : static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
    2279             :                                 int blank)
    2280           1 : {
    2281           1 :         struct fb_event event;
    2282           1 : 
    2283           3 :         if (blank) {
    2284           7 :                 unsigned short charmask = vc->vc_hi_font_mask ?
    2285             :                         0x1ff : 0xff;
    2286             :                 unsigned short oldc;
    2287             : 
    2288           1 :                 oldc = vc->vc_video_erase_char;
    2289           2 :                 vc->vc_video_erase_char &= charmask;
    2290           6 :                 fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols);
    2291           1 :                 vc->vc_video_erase_char = oldc;
    2292             :         }
    2293             : 
    2294             : 
    2295           6 :         if (!lock_fb_info(info))
    2296           2 :                 return;
    2297           2 :         event.info = info;
    2298           2 :         event.data = &blank;
    2299           2 :         fb_notifier_call_chain(FB_EVENT_CONBLANK, &event);
    2300           4 :         unlock_fb_info(info);
    2301           1 : }
    2302             : 
    2303             : static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
    2304             : {
    2305           3 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    2306           3 :         struct fbcon_ops *ops = info->fbcon_par;
    2307           1 : 
    2308           3 :         if (mode_switch) {
    2309           2 :                 struct fb_var_screeninfo var = info->var;
    2310           1 : 
    2311           1 :                 ops->graphics = 1;
    2312             : 
    2313           2 :                 if (!blank) {
    2314           1 :                         var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
    2315           1 :                         fb_set_var(info, &var);
    2316           1 :                         ops->graphics = 0;
    2317           1 :                         ops->var = info->var;
    2318             :                 }
    2319             :         }
    2320             : 
    2321           4 :         if (!fbcon_is_inactive(vc, info)) {
    2322           2 :                 if (ops->blank_state != blank) {
    2323           1 :                         ops->blank_state = blank;
    2324           8 :                         fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
    2325           1 :                         ops->cursor_flash = (!blank);
    2326             : 
    2327           2 :                         if (!(info->flags & FBINFO_MISC_USEREVENT))
    2328           3 :                                 if (fb_blank(info, blank))
    2329           4 :                                         fbcon_generic_blank(vc, info, blank);
    2330             :                 }
    2331             : 
    2332           6 :                 if (!blank)
    2333           3 :                         update_screen(vc);
    2334             :         }
    2335             : 
    2336          16 :         if (mode_switch || fbcon_is_inactive(vc, info) ||
    2337             :             ops->blank_state != FB_BLANK_UNBLANK)
    2338          15 :                 fbcon_del_cursor_timer(info);
    2339             :         else
    2340           3 :                 fbcon_add_cursor_timer(info);
    2341             : 
    2342           4 :         return 0;
    2343             : }
    2344             : 
    2345             : static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
    2346             : {
    2347           2 :         u8 *fontdata = vc->vc_font.data;
    2348           2 :         u8 *data = font->data;
    2349           1 :         int i, j;
    2350           1 : 
    2351           2 :         font->width = vc->vc_font.width;
    2352           2 :         font->height = vc->vc_font.height;
    2353           8 :         font->charcount = vc->vc_hi_font_mask ? 512 : 256;
    2354           3 :         if (!font->data)
    2355           1 :                 return 0;
    2356             : 
    2357           2 :         if (font->width <= 8) {
    2358           1 :                 j = vc->vc_font.height;
    2359           5 :                 for (i = 0; i < font->charcount; i++) {
    2360           2 :                         memcpy(data, fontdata, j);
    2361           3 :                         memset(data + j, 0, 32 - j);
    2362           1 :                         data += 32;
    2363           1 :                         fontdata += j;
    2364             :                 }
    2365           2 :         } else if (font->width <= 16) {
    2366           1 :                 j = vc->vc_font.height * 2;
    2367           5 :                 for (i = 0; i < font->charcount; i++) {
    2368           2 :                         memcpy(data, fontdata, j);
    2369           3 :                         memset(data + j, 0, 64 - j);
    2370           1 :                         data += 64;
    2371           1 :                         fontdata += j;
    2372             :                 }
    2373           2 :         } else if (font->width <= 24) {
    2374           5 :                 for (i = 0; i < font->charcount; i++) {
    2375           6 :                         for (j = 0; j < vc->vc_font.height; j++) {
    2376           7 :                                 *data++ = fontdata[0];
    2377           4 :                                 *data++ = fontdata[1];
    2378           3 :                                 *data++ = fontdata[2];
    2379           1 :                                 fontdata += sizeof(u32);
    2380             :                         }
    2381           1 :                         memset(data, 0, 3 * (32 - j));
    2382           1 :                         data += 3 * (32 - j);
    2383             :                 }
    2384             :         } else {
    2385           1 :                 j = vc->vc_font.height * 4;
    2386           5 :                 for (i = 0; i < font->charcount; i++) {
    2387           2 :                         memcpy(data, fontdata, j);
    2388           3 :                         memset(data + j, 0, 128 - j);
    2389           1 :                         data += 128;
    2390           1 :                         fontdata += j;
    2391             :                 }
    2392             :         }
    2393           4 :         return 0;
    2394             : }
    2395             : 
    2396             : static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
    2397             :                              const u8 * data, int userfont)
    2398             : {
    2399           9 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    2400           9 :         struct fbcon_ops *ops = info->fbcon_par;
    2401           9 :         struct display *p = &fb_display[vc->vc_num];
    2402           3 :         int resize;
    2403           3 :         int cnt;
    2404           6 :         char *old_data = NULL;
    2405           3 : 
    2406          15 :         if (CON_IS_VISIBLE(vc) && softback_lines)
    2407          12 :                 fbcon_set_origin(vc);
    2408           3 : 
    2409          39 :         resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
    2410          15 :         if (p->userfont)
    2411           9 :                 old_data = vc->vc_font.data;
    2412          15 :         if (userfont)
    2413           9 :                 cnt = FNTCHARCNT(data);
    2414           3 :         else
    2415           9 :                 cnt = 256;
    2416          21 :         vc->vc_font.data = (void *)(p->fontdata = data);
    2417          27 :         if ((p->userfont = userfont))
    2418           9 :                 REFCOUNT(data)++;
    2419           9 :         vc->vc_font.width = w;
    2420           6 :         vc->vc_font.height = h;
    2421          30 :         if (vc->vc_hi_font_mask && cnt == 256) {
    2422           6 :                 vc->vc_hi_font_mask = 0;
    2423          12 :                 if (vc->vc_can_do_color) {
    2424          12 :                         vc->vc_complement_mask >>= 1;
    2425          12 :                         vc->vc_s_complement_mask >>= 1;
    2426             :                 }
    2427             :                         
    2428             :                 /* ++Edmund: reorder the attribute bits */
    2429          12 :                 if (vc->vc_can_do_color) {
    2430          12 :                         unsigned short *cp =
    2431             :                             (unsigned short *) vc->vc_origin;
    2432           6 :                         int count = vc->vc_screenbuf_size / 2;
    2433             :                         unsigned short c;
    2434          18 :                         for (; count > 0; count--, cp++) {
    2435           9 :                                 c = scr_readw(cp);
    2436           6 :                                 scr_writew(((c & 0xfe00) >> 1) |
    2437             :                                            (c & 0xff), cp);
    2438             :                         }
    2439           3 :                         c = vc->vc_video_erase_char;
    2440           3 :                         vc->vc_video_erase_char =
    2441             :                             ((c & 0xfe00) >> 1) | (c & 0xff);
    2442           6 :                         vc->vc_attr >>= 1;
    2443             :                 }
    2444          30 :         } else if (!vc->vc_hi_font_mask && cnt == 512) {
    2445           6 :                 vc->vc_hi_font_mask = 0x100;
    2446          12 :                 if (vc->vc_can_do_color) {
    2447          12 :                         vc->vc_complement_mask <<= 1;
    2448          12 :                         vc->vc_s_complement_mask <<= 1;
    2449             :                 }
    2450             :                         
    2451             :                 /* ++Edmund: reorder the attribute bits */
    2452             :                 {
    2453          12 :                         unsigned short *cp =
    2454             :                             (unsigned short *) vc->vc_origin;
    2455           6 :                         int count = vc->vc_screenbuf_size / 2;
    2456             :                         unsigned short c;
    2457          18 :                         for (; count > 0; count--, cp++) {
    2458           6 :                                 unsigned short newc;
    2459           6 :                                 c = scr_readw(cp);
    2460           6 :                                 if (vc->vc_can_do_color)
    2461           3 :                                         newc =
    2462             :                                             ((c & 0xff00) << 1) | (c &
    2463             :                                                                    0xff);
    2464             :                                 else
    2465           3 :                                         newc = c & ~0x100;
    2466           3 :                                 scr_writew(newc, cp);
    2467             :                         }
    2468           3 :                         c = vc->vc_video_erase_char;
    2469           6 :                         if (vc->vc_can_do_color) {
    2470           3 :                                 vc->vc_video_erase_char =
    2471             :                                     ((c & 0xff00) << 1) | (c & 0xff);
    2472           6 :                                 vc->vc_attr <<= 1;
    2473             :                         } else
    2474           3 :                                 vc->vc_video_erase_char = c & ~0x100;
    2475             :                 }
    2476             : 
    2477             :         }
    2478             : 
    2479          24 :         if (resize) {
    2480             :                 int cols, rows;
    2481             : 
    2482         120 :                 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
    2483         120 :                 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
    2484          12 :                 cols /= w;
    2485          12 :                 rows /= h;
    2486          12 :                 vc_resize(vc, cols, rows);
    2487          48 :                 if (CON_IS_VISIBLE(vc) && softback_buf)
    2488          24 :                         fbcon_update_softback(vc);
    2489          60 :         } else if (CON_IS_VISIBLE(vc)
    2490             :                    && vc->vc_mode == KD_TEXT) {
    2491          24 :                 fbcon_clear_margins(vc, 0);
    2492           3 :                 update_screen(vc);
    2493             :         }
    2494             : 
    2495          90 :         if (old_data && (--REFCOUNT(old_data) == 0))
    2496          18 :                 kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
    2497          18 :         return 0;
    2498             : }
    2499             : 
    2500             : static int fbcon_copy_font(struct vc_data *vc, int con)
    2501             : {
    2502           2 :         struct display *od = &fb_display[con];
    2503           2 :         struct console_font *f = &vc->vc_font;
    2504           1 : 
    2505           4 :         if (od->fontdata == f->data)
    2506           1 :                 return 0;       /* already the same font... */
    2507          10 :         return fbcon_do_set_font(vc, f->width, f->height, od->fontdata, od->userfont);
    2508             : }
    2509             : 
    2510             : /*
    2511             :  *  User asked to set font; we are guaranteed that
    2512             :  *      a) width and height are in range 1..32
    2513             :  *      b) charcount does not exceed 512
    2514             :  *  but lets not assume that, since someone might someday want to use larger
    2515             :  *  fonts. And charcount of 512 is small for unicode support.
    2516             :  *
    2517             :  *  However, user space gives the font in 32 rows , regardless of
    2518             :  *  actual font height. So a new API is needed if support for larger fonts
    2519             :  *  is ever implemented.
    2520             :  */
    2521             : 
    2522             : static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags)
    2523             : {
    2524           3 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    2525           2 :         unsigned charcount = font->charcount;
    2526           3 :         int w = font->width;
    2527           3 :         int h = font->height;
    2528           1 :         int size;
    2529           1 :         int i, csum;
    2530           2 :         u8 *new_data, *data = font->data;
    2531           2 :         int pitch = (font->width+7) >> 3;
    2532           1 : 
    2533           1 :         /* Is there a reason why fbconsole couldn't handle any charcount >256?
    2534           1 :          * If not this check should be changed to charcount < 256 */
    2535           5 :         if (charcount != 256 && charcount != 512)
    2536           2 :                 return -EINVAL;
    2537           1 : 
    2538           1 :         /* Make sure drawing engine can handle the font */
    2539           5 :         if (!(info->pixmap.blit_x & (1 << (font->width - 1))) ||
    2540             :             !(info->pixmap.blit_y & (1 << (font->height - 1))))
    2541           1 :                 return -EINVAL;
    2542             : 
    2543             :         /* Make sure driver can handle the font length */
    2544           4 :         if (fbcon_invalid_charcount(info, charcount))
    2545           1 :                 return -EINVAL;
    2546             : 
    2547           1 :         size = h * pitch * charcount;
    2548             : 
    2549           3 :         new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER);
    2550             : 
    2551           2 :         if (!new_data)
    2552           1 :                 return -ENOMEM;
    2553             : 
    2554           1 :         new_data += FONT_EXTRA_WORDS * sizeof(int);
    2555           1 :         FNTSIZE(new_data) = size;
    2556           1 :         FNTCHARCNT(new_data) = charcount;
    2557           1 :         REFCOUNT(new_data) = 0; /* usage counter */
    2558           5 :         for (i=0; i< charcount; i++) {
    2559           2 :                 memcpy(new_data + i*h*pitch, data +  i*32*pitch, h*pitch);
    2560           2 :         }
    2561             : 
    2562             :         /* Since linux has a nice crc32 function use it for counting font
    2563             :          * checksums. */
    2564           2 :         csum = crc32(0, new_data, size);
    2565             : 
    2566           1 :         FNTSUM(new_data) = csum;
    2567             :         /* Check if the same font is on some other console already */
    2568           5 :         for (i = first_fb_vc; i <= last_fb_vc; i++) {
    2569           3 :                 struct vc_data *tmp = vc_cons[i].d;
    2570           1 :                 
    2571          13 :                 if (fb_display[i].userfont &&
    2572             :                     fb_display[i].fontdata &&
    2573             :                     FNTSUM(fb_display[i].fontdata) == csum &&
    2574           1 :                     FNTSIZE(fb_display[i].fontdata) == size &&
    2575             :                     tmp->vc_font.width == w &&
    2576             :                     !memcmp(fb_display[i].fontdata, new_data, size)) {
    2577           1 :                         kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
    2578           1 :                         new_data = (u8 *)fb_display[i].fontdata;
    2579           1 :                         break;
    2580             :                 }
    2581             :         }
    2582          10 :         return fbcon_do_set_font(vc, font->width, font->height, new_data, 1);
    2583             : }
    2584             : 
    2585             : static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name)
    2586             : {
    2587           3 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    2588           1 :         const struct font_desc *f;
    2589           1 : 
    2590           2 :         if (!name)
    2591           1 :                 f = get_default_font(info->var.xres, info->var.yres,
    2592             :                                      info->pixmap.blit_x, info->pixmap.blit_y);
    2593           3 :         else if (!(f = find_font(name)))
    2594           1 :                 return -ENOENT;
    2595             : 
    2596           2 :         font->width = f->width;
    2597           2 :         font->height = f->height;
    2598           9 :         return fbcon_do_set_font(vc, f->width, f->height, f->data, 0);
    2599             : }
    2600             : 
    2601           1 : static u16 palette_red[16];
    2602           1 : static u16 palette_green[16];
    2603           1 : static u16 palette_blue[16];
    2604             : 
    2605           1 : static struct fb_cmap palette_cmap = {
    2606             :         0, 16, palette_red, palette_green, palette_blue, NULL
    2607             : };
    2608             : 
    2609             : static int fbcon_set_palette(struct vc_data *vc, unsigned char *table)
    2610             : {
    2611          12 :         struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
    2612           4 :         int i, j, k, depth;
    2613           4 :         u8 val;
    2614           4 : 
    2615          20 :         if (fbcon_is_inactive(vc, info))
    2616           8 :                 return -EINVAL;
    2617           4 : 
    2618          12 :         if (!CON_IS_VISIBLE(vc))
    2619           8 :                 return 0;
    2620           4 : 
    2621           8 :         depth = fb_get_color_depth(&info->var, &info->fix);
    2622          12 :         if (depth > 3) {
    2623          24 :                 for (i = j = 0; i < 16; i++) {
    2624          12 :                         k = table[i];
    2625          16 :                         val = vc->vc_palette[j++];
    2626           4 :                         palette_red[k] = (val << 8) | val;
    2627          12 :                         val = vc->vc_palette[j++];
    2628           4 :                         palette_green[k] = (val << 8) | val;
    2629          12 :                         val = vc->vc_palette[j++];
    2630           4 :                         palette_blue[k] = (val << 8) | val;
    2631             :                 }
    2632           4 :                 palette_cmap.len = 16;
    2633           4 :                 palette_cmap.start = 0;
    2634             :         /*
    2635             :          * If framebuffer is capable of less than 16 colors,
    2636             :          * use default palette of fbcon.
    2637             :          */
    2638             :         } else
    2639           8 :                 fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap);
    2640             : 
    2641          16 :         return fb_set_cmap(&palette_cmap, info);
    2642             : }
    2643             : 
    2644             : static u16 *fbcon_screen_pos(struct vc_data *vc, int offset)
    2645             : {
    2646           1 :         unsigned long p;
    2647           1 :         int line;
    2648             :         
    2649           5 :         if (vc->vc_num != fg_console || !softback_lines)
    2650           1 :                 return (u16 *) (vc->vc_origin + offset);
    2651           1 :         line = offset / vc->vc_size_row;
    2652           2 :         if (line >= softback_lines)
    2653           1 :                 return (u16 *) (vc->vc_origin + offset -
    2654             :                                 softback_lines * vc->vc_size_row);
    2655           1 :         p = softback_curr + offset;
    2656           2 :         if (p >= softback_end)
    2657           1 :                 p += softback_buf - softback_end;
    2658           1 :         return (u16 *) p;
    2659             : }
    2660             : 
    2661             : static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
    2662             :                                  int *px, int *py)
    2663             : {
    2664           1 :         unsigned long ret;
    2665           1 :         int x, y;
    2666           1 : 
    2667           5 :         if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
    2668           2 :                 unsigned long offset = (pos - vc->vc_origin) / 2;
    2669             : 
    2670           2 :                 x = offset % vc->vc_cols;
    2671           2 :                 y = offset / vc->vc_cols;
    2672           3 :                 if (vc->vc_num == fg_console)
    2673           1 :                         y += softback_lines;
    2674           1 :                 ret = pos + (vc->vc_cols - x) * 2;
    2675           5 :         } else if (vc->vc_num == fg_console && softback_lines) {
    2676           1 :                 unsigned long offset = pos - softback_curr;
    2677             : 
    2678           2 :                 if (pos < softback_curr)
    2679           1 :                         offset += softback_end - softback_buf;
    2680           1 :                 offset /= 2;
    2681           2 :                 x = offset % vc->vc_cols;
    2682           2 :                 y = offset / vc->vc_cols;
    2683           1 :                 ret = pos + (vc->vc_cols - x) * 2;
    2684           2 :                 if (ret == softback_end)
    2685           1 :                         ret = softback_buf;
    2686           2 :                 if (ret == softback_in)
    2687           1 :                         ret = vc->vc_origin;
    2688             :         } else {
    2689             :                 /* Should not happen */
    2690           2 :                 x = y = 0;
    2691           1 :                 ret = vc->vc_origin;
    2692             :         }
    2693           2 :         if (px)
    2694           1 :                 *px = x;
    2695           2 :         if (py)
    2696           1 :                 *py = y;
    2697           1 :         return ret;
    2698             : }
    2699             : 
    2700             : /* As we might be inside of softback, we may work with non-contiguous buffer,
    2701             :    that's why we have to use a separate routine. */
    2702             : static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
    2703             : {
    2704           6 :         while (cnt--) {
    2705           3 :                 u16 a = scr_readw(p);
    2706           4 :                 if (!vc->vc_can_do_color)
    2707           1 :                         a ^= 0x0800;
    2708           3 :                 else if (vc->vc_hi_font_mask == 0x100)
    2709           1 :                         a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
    2710             :                             (((a) & 0x0e00) << 4);
    2711           1 :                 else
    2712           1 :                         a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
    2713             :                             (((a) & 0x0700) << 4);
    2714           3 :                 scr_writew(a, p++);
    2715           2 :                 if (p == (u16 *) softback_end)
    2716           1 :                         p = (u16 *) softback_buf;
    2717           2 :                 if (p == (u16 *) softback_in)
    2718           2 :                         p = (u16 *) vc->vc_origin;
    2719             :         }
    2720             : }
    2721             : 
    2722           1 : static int fbcon_scrolldelta(struct vc_data *vc, int lines)
    2723             : {
    2724          28 :         struct fb_info *info = registered_fb[con2fb_map[fg_console]];
    2725          42 :         struct fbcon_ops *ops = info->fbcon_par;
    2726          28 :         struct display *disp = &fb_display[fg_console];
    2727          14 :         int offset, limit, scrollback_old;
    2728          14 : 
    2729          42 :         if (softback_top) {
    2730          56 :                 if (vc->vc_num != fg_console)
    2731          28 :                         return 0;
    2732          84 :                 if (vc->vc_mode != KD_TEXT || !lines)
    2733          28 :                         return 0;
    2734          42 :                 if (logo_shown >= 0) {
    2735          14 :                         struct vc_data *conp2 = vc_cons[logo_shown].d;
    2736             : 
    2737          56 :                         if (conp2->vc_top == logo_lines
    2738             :                             && conp2->vc_bottom == conp2->vc_rows)
    2739          14 :                                 conp2->vc_top = 0;
    2740          42 :                         if (logo_shown == vc->vc_num) {
    2741             :                                 unsigned long p, q;
    2742             :                                 int i;
    2743             : 
    2744          14 :                                 p = softback_in;
    2745          14 :                                 q = vc->vc_origin +
    2746             :                                     logo_lines * vc->vc_size_row;
    2747          70 :                                 for (i = 0; i < logo_lines; i++) {
    2748          42 :                                         if (p == softback_top)
    2749          42 :                                                 break;
    2750          28 :                                         if (p == softback_buf)
    2751          14 :                                                 p = softback_end;
    2752          28 :                                         p -= vc->vc_size_row;
    2753          42 :                                         q -= vc->vc_size_row;
    2754          28 :                                         scr_memcpyw((u16 *) q, (u16 *) p,
    2755             :                                                     vc->vc_size_row);
    2756             :                                 }
    2757          28 :                                 softback_in = softback_curr = p;
    2758          14 :                                 update_region(vc, vc->vc_origin,
    2759             :                                               logo_lines * vc->vc_cols);
    2760             :                         }
    2761          28 :                         logo_shown = FBCON_LOGO_CANSHOW;
    2762             :                 }
    2763          52 :                 fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK);
    2764          42 :                 fbcon_redraw_softback(vc, disp, lines);
    2765          26 :                 fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK);
    2766          14 :                 return 0;
    2767             :         }
    2768             : 
    2769          28 :         if (!scrollback_phys_max)
    2770          14 :                 return -ENOSYS;
    2771             : 
    2772          14 :         scrollback_old = scrollback_current;
    2773          14 :         scrollback_current -= lines;
    2774          28 :         if (scrollback_current < 0)
    2775          14 :                 scrollback_current = 0;
    2776          28 :         else if (scrollback_current > scrollback_max)
    2777          14 :                 scrollback_current = scrollback_max;
    2778          28 :         if (scrollback_current == scrollback_old)
    2779          14 :                 return 0;
    2780             : 
    2781          56 :         if (fbcon_is_inactive(vc, info))
    2782          14 :                 return 0;
    2783             : 
    2784          26 :         fbcon_cursor(vc, CM_ERASE);
    2785             : 
    2786          28 :         offset = disp->yscroll - scrollback_current;
    2787          14 :         limit = disp->vrows;
    2788          14 :         switch (disp->scrollmode) {
    2789          70 :         case SCROLL_WRAP_MOVE:
    2790          14 :                 info->var.vmode |= FB_VMODE_YWRAP;
    2791          14 :                 break;
    2792          70 :         case SCROLL_PAN_MOVE:
    2793          70 :         case SCROLL_PAN_REDRAW:
    2794          14 :                 limit -= vc->vc_rows;
    2795          14 :                 info->var.vmode &= ~FB_VMODE_YWRAP;
    2796          14 :                 break;
    2797          14 :         }
    2798          28 :         if (offset < 0)
    2799          28 :                 offset += limit;
    2800          28 :         else if (offset >= limit)
    2801          14 :                 offset -= limit;
    2802             : 
    2803          14 :         ops->var.xoffset = 0;
    2804          14 :         ops->var.yoffset = offset * vc->vc_font.height;
    2805          14 :         ops->update_start(info);
    2806             : 
    2807          28 :         if (!scrollback_current)
    2808          26 :                 fbcon_cursor(vc, CM_DRAW);
    2809          28 :         return 0;
    2810             : }
    2811             : 
    2812             : static int fbcon_set_origin(struct vc_data *vc)
    2813             : {
    2814          46 :         if (softback_lines)
    2815          88 :                 fbcon_scrolldelta(vc, softback_lines);
    2816          46 :         return 0;
    2817             : }
    2818             : 
    2819             : static void fbcon_suspended(struct fb_info *info)
    2820             : {
    2821           2 :         struct vc_data *vc = NULL;
    2822           3 :         struct fbcon_ops *ops = info->fbcon_par;
    2823             : 
    2824           4 :         if (!ops || ops->currcon < 0)
    2825           1 :                 return;
    2826           1 :         vc = vc_cons[ops->currcon].d;
    2827             : 
    2828             :         /* Clear cursor, restore saved data */
    2829           3 :         fbcon_cursor(vc, CM_ERASE);
    2830           1 : }
    2831             : 
    2832             : static void fbcon_resumed(struct fb_info *info)
    2833             : {
    2834           1 :         struct vc_data *vc;
    2835           3 :         struct fbcon_ops *ops = info->fbcon_par;
    2836             : 
    2837           4 :         if (!ops || ops->currcon < 0)
    2838           1 :                 return;
    2839           1 :         vc = vc_cons[ops->currcon].d;
    2840             : 
    2841           1 :         update_screen(vc);
    2842           1 : }
    2843             : 
    2844             : static void fbcon_modechanged(struct fb_info *info)
    2845             : {
    2846           6 :         struct fbcon_ops *ops = info->fbcon_par;
    2847           2 :         struct vc_data *vc;
    2848           2 :         struct display *p;
    2849           2 :         int rows, cols;
    2850           2 : 
    2851          10 :         if (!ops || ops->currcon < 0)
    2852           4 :                 return;
    2853           4 :         vc = vc_cons[ops->currcon].d;
    2854          12 :         if (vc->vc_mode != KD_TEXT ||
    2855           2 :             registered_fb[con2fb_map[ops->currcon]] != info)
    2856           4 :                 return;
    2857             : 
    2858           4 :         p = &fb_display[vc->vc_num];
    2859           4 :         set_blitting_type(vc, info);
    2860             : 
    2861           4 :         if (CON_IS_VISIBLE(vc)) {
    2862           4 :                 var_to_display(p, &info->var, info);
    2863          20 :                 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
    2864          20 :                 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
    2865           2 :                 cols /= vc->vc_font.width;
    2866           2 :                 rows /= vc->vc_font.height;
    2867           2 :                 vc_resize(vc, cols, rows);
    2868           4 :                 updatescrollmode(p, info, vc);
    2869           2 :                 scrollback_max = 0;
    2870           2 :                 scrollback_current = 0;
    2871             : 
    2872           8 :                 if (!fbcon_is_inactive(vc, info)) {
    2873           8 :                     ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
    2874           2 :                     ops->update_start(info);
    2875             :                 }
    2876             : 
    2877           6 :                 fbcon_set_palette(vc, color_table);
    2878           2 :                 update_screen(vc);
    2879           4 :                 if (softback_buf)
    2880           4 :                         fbcon_update_softback(vc);
    2881             :         }
    2882           6 : }
    2883             : 
    2884             : static void fbcon_set_all_vcs(struct fb_info *info)
    2885             : {
    2886           3 :         struct fbcon_ops *ops = info->fbcon_par;
    2887           1 :         struct vc_data *vc;
    2888           1 :         struct display *p;
    2889           2 :         int i, rows, cols, fg = -1;
    2890           1 : 
    2891           5 :         if (!ops || ops->currcon < 0)
    2892           2 :                 return;
    2893           1 : 
    2894           7 :         for (i = first_fb_vc; i <= last_fb_vc; i++) {
    2895           5 :                 vc = vc_cons[i].d;
    2896           9 :                 if (!vc || vc->vc_mode != KD_TEXT ||
    2897             :                     registered_fb[con2fb_map[i]] != info)
    2898           1 :                         continue;
    2899             : 
    2900           2 :                 if (CON_IS_VISIBLE(vc)) {
    2901           1 :                         fg = i;
    2902           1 :                         continue;
    2903             :                 }
    2904             : 
    2905           2 :                 p = &fb_display[vc->vc_num];
    2906           2 :                 set_blitting_type(vc, info);
    2907           2 :                 var_to_display(p, &info->var, info);
    2908          10 :                 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
    2909          10 :                 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
    2910           1 :                 cols /= vc->vc_font.width;
    2911           1 :                 rows /= vc->vc_font.height;
    2912           1 :                 vc_resize(vc, cols, rows);
    2913             :         }
    2914           1 : 
    2915           2 :         if (fg != -1)
    2916           5 :                 fbcon_modechanged(info);
    2917           2 : }
    2918             : 
    2919             : static int fbcon_mode_deleted(struct fb_info *info,
    2920             :                               struct fb_videomode *mode)
    2921           1 : {
    2922           1 :         struct fb_info *fb_info;
    2923           1 :         struct display *p;
    2924           2 :         int i, j, found = 0;
    2925           1 : 
    2926           1 :         /* before deletion, ensure that mode is not in use */
    2927           5 :         for (i = first_fb_vc; i <= last_fb_vc; i++) {
    2928           3 :                 j = con2fb_map[i];
    2929           3 :                 if (j == -1)
    2930           1 :                         continue;
    2931           1 :                 fb_info = registered_fb[j];
    2932           2 :                 if (fb_info != info)
    2933           2 :                         continue;
    2934           1 :                 p = &fb_display[i];
    2935           5 :                 if (!p || !p->mode)
    2936           1 :                         continue;
    2937           3 :                 if (fb_mode_is_equal(p->mode, mode)) {
    2938           1 :                         found = 1;
    2939           1 :                         break;
    2940             :                 }
    2941             :         }
    2942           1 :         return found;
    2943           1 : }
    2944             : 
    2945             : #ifdef CONFIG_VT_HW_CONSOLE_BINDING
    2946             : static int fbcon_unbind(void)
    2947             : {
    2948             :         int ret;
    2949             : 
    2950             :         ret = unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc,
    2951             :                                 fbcon_is_default);
    2952             : 
    2953             :         if (!ret)
    2954             :                 fbcon_has_console_bind = 0;
    2955             : 
    2956             :         return ret;
    2957             : }
    2958             : #else
    2959             : static inline int fbcon_unbind(void)
    2960             : {
    2961           1 :         return -EINVAL;
    2962             : }
    2963             : #endif /* CONFIG_VT_HW_CONSOLE_BINDING */
    2964             : 
    2965             : static int fbcon_fb_unbind(int idx)
    2966             : {
    2967           3 :         int i, new_idx = -1, ret = 0;
    2968           1 : 
    2969           3 :         if (!fbcon_has_console_bind)
    2970           1 :                 return 0;
    2971             : 
    2972           5 :         for (i = first_fb_vc; i <= last_fb_vc; i++) {
    2973           6 :                 if (con2fb_map[i] != idx &&
    2974           1 :                     con2fb_map[i] != -1) {
    2975           1 :                         new_idx = i;
    2976           1 :                         break;
    2977             :                 }
    2978           1 :         }
    2979             : 
    2980           2 :         if (new_idx != -1) {
    2981           6 :                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
    2982           5 :                         if (con2fb_map[i] == idx)
    2983           5 :                                 set_con2fb_map(i, new_idx, 0);
    2984             :                 }
    2985             :         } else
    2986           2 :                 ret = fbcon_unbind();
    2987             : 
    2988           2 :         return ret;
    2989             : }
    2990             : 
    2991             : static int fbcon_fb_unregistered(struct fb_info *info)
    2992             : {
    2993           1 :         int i, idx;
    2994           1 : 
    2995           1 :         idx = info->node;
    2996           5 :         for (i = first_fb_vc; i <= last_fb_vc; i++) {
    2997           4 :                 if (con2fb_map[i] == idx)
    2998           2 :                         con2fb_map[i] = -1;
    2999             :         }
    3000             : 
    3001           2 :         if (idx == info_idx) {
    3002           1 :                 info_idx = -1;
    3003             : 
    3004           5 :                 for (i = 0; i < FB_MAX; i++) {
    3005           4 :                         if (registered_fb[i] != NULL) {
    3006           2 :                                 info_idx = i;
    3007           1 :                                 break;
    3008             :                         }
    3009             :                 }
    3010           1 :         }
    3011             : 
    3012           4 :         if (info_idx != -1) {
    3013           7 :                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
    3014           4 :                         if (con2fb_map[i] == -1)
    3015           2 :                                 con2fb_map[i] = info_idx;
    3016             :                 }
    3017             :         }
    3018             : 
    3019           6 :         if (primary_device == idx)
    3020           3 :                 primary_device = -1;
    3021             : 
    3022           6 :         if (!num_registered_fb)
    3023           3 :                 unregister_con_driver(&fb_con);
    3024             : 
    3025           3 :         return 0;
    3026             : }
    3027             : 
    3028             : #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
    3029             : static void fbcon_select_primary(struct fb_info *info)
    3030             : {
    3031           9 :         if (!map_override && primary_device == -1 &&
    3032           1 :             fb_is_primary_device(info)) {
    3033           1 :                 int i;
    3034             : 
    3035           1 :                 printk(KERN_INFO "fbcon: %s (fb%i) is primary device\n",
    3036             :                        info->fix.id, info->node);
    3037           1 :                 primary_device = info->node;
    3038             : 
    3039           5 :                 for (i = first_fb_vc; i <= last_fb_vc; i++)
    3040           3 :                         con2fb_map_boot[i] = primary_device;
    3041           1 : 
    3042           3 :                 if (con_is_bound(&fb_con)) {
    3043           1 :                         printk(KERN_INFO "fbcon: Remapping primary device, "
    3044             :                                "fb%i, to tty %i-%i\n", info->node,
    3045             :                                first_fb_vc + 1, last_fb_vc + 1);
    3046           1 :                         info_idx = primary_device;
    3047             :                 }
    3048             :         }
    3049           3 : 
    3050             : }
    3051             : #else
    3052             : static inline void fbcon_select_primary(struct fb_info *info)
    3053             : {
    3054             :         return;
    3055             : }
    3056             : #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */
    3057             : 
    3058             : static int fbcon_fb_registered(struct fb_info *info)
    3059             : {
    3060           2 :         int ret = 0, i, idx;
    3061           1 : 
    3062           2 :         idx = info->node;
    3063           4 :         fbcon_select_primary(info);
    3064             : 
    3065           2 :         if (info_idx == -1) {
    3066           5 :                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
    3067           4 :                         if (con2fb_map_boot[i] == idx) {
    3068           2 :                                 info_idx = idx;
    3069           1 :                                 break;
    3070             :                         }
    3071             :                 }
    3072           1 : 
    3073           2 :                 if (info_idx != -1)
    3074           4 :                         ret = fbcon_takeover(1);
    3075             :         } else {
    3076           6 :                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
    3077           5 :                         if (con2fb_map_boot[i] == idx)
    3078           5 :                                 set_con2fb_map(i, idx, 0);
    3079             :                 }
    3080             :         }
    3081             : 
    3082           3 :         return ret;
    3083             : }
    3084             : 
    3085             : static void fbcon_fb_blanked(struct fb_info *info, int blank)
    3086             : {
    3087           3 :         struct fbcon_ops *ops = info->fbcon_par;
    3088           1 :         struct vc_data *vc;
    3089             : 
    3090           4 :         if (!ops || ops->currcon < 0)
    3091           1 :                 return;
    3092             : 
    3093           1 :         vc = vc_cons[ops->currcon].d;
    3094           5 :         if (vc->vc_mode != KD_TEXT ||
    3095             :                         registered_fb[con2fb_map[ops->currcon]] != info)
    3096           1 :                 return;
    3097             : 
    3098           2 :         if (CON_IS_VISIBLE(vc)) {
    3099           2 :                 if (blank)
    3100           1 :                         do_blank_screen(0);
    3101             :                 else
    3102           1 :                         do_unblank_screen(0);
    3103             :         }
    3104           1 :         ops->blank_state = blank;
    3105           1 : }
    3106             : 
    3107             : static void fbcon_new_modelist(struct fb_info *info)
    3108             : {
    3109           1 :         int i;
    3110           1 :         struct vc_data *vc;
    3111           1 :         struct fb_var_screeninfo var;
    3112           1 :         const struct fb_videomode *mode;
    3113             : 
    3114           6 :         for (i = first_fb_vc; i <= last_fb_vc; i++) {
    3115           5 :                 if (registered_fb[con2fb_map[i]] != info)
    3116           2 :                         continue;
    3117           2 :                 if (!fb_display[i].mode)
    3118           1 :                         continue;
    3119           1 :                 vc = vc_cons[i].d;
    3120           2 :                 display_to_var(&var, &fb_display[i]);
    3121           2 :                 mode = fb_find_nearest_mode(fb_display[i].mode,
    3122             :                                             &info->modelist);
    3123           1 :                 fb_videomode_to_var(&var, mode);
    3124           4 :                 fbcon_set_disp(info, &var, vc->vc_num);
    3125             :         }
    3126           1 : }
    3127             : 
    3128             : static void fbcon_get_requirement(struct fb_info *info,
    3129             :                                   struct fb_blit_caps *caps)
    3130           1 : {
    3131           1 :         struct vc_data *vc;
    3132           1 :         struct display *p;
    3133           1 : 
    3134           2 :         if (caps->flags) {
    3135             :                 int i, charcnt;
    3136             : 
    3137           5 :                 for (i = first_fb_vc; i <= last_fb_vc; i++) {
    3138           3 :                         vc = vc_cons[i].d;
    3139           8 :                         if (vc && vc->vc_mode == KD_TEXT &&
    3140             :                             info->node == con2fb_map[i]) {
    3141           1 :                                 p = &fb_display[i];
    3142           1 :                                 caps->x |= 1 << (vc->vc_font.width - 1);
    3143           1 :                                 caps->y |= 1 << (vc->vc_font.height - 1);
    3144           6 :                                 charcnt = (p->userfont) ?
    3145             :                                         FNTCHARCNT(p->fontdata) : 256;
    3146           2 :                                 if (caps->len < charcnt)
    3147           1 :                                         caps->len = charcnt;
    3148             :                         }
    3149             :                 }
    3150             :         } else {
    3151           1 :                 vc = vc_cons[fg_console].d;
    3152             : 
    3153           7 :                 if (vc && vc->vc_mode == KD_TEXT &&
    3154             :                     info->node == con2fb_map[fg_console]) {
    3155           1 :                         p = &fb_display[fg_console];
    3156           1 :                         caps->x = 1 << (vc->vc_font.width - 1);
    3157           1 :                         caps->y = 1 << (vc->vc_font.height - 1);
    3158           6 :                         caps->len = (p->userfont) ?
    3159             :                                 FNTCHARCNT(p->fontdata) : 256;
    3160             :                 }
    3161             :         }
    3162           2 : }
    3163             : 
    3164             : static int fbcon_event_notify(struct notifier_block *self,
    3165             :                               unsigned long action, void *data)
    3166             : {
    3167           2 :         struct fb_event *event = data;
    3168           2 :         struct fb_info *info = event->info;
    3169           1 :         struct fb_videomode *mode;
    3170           1 :         struct fb_con2fbmap *con2fb;
    3171           1 :         struct fb_blit_caps *caps;
    3172           2 :         int idx, ret = 0;
    3173           1 : 
    3174             :         /*
    3175             :          * ignore all events except driver registration and deregistration
    3176             :          * if fbcon is not active
    3177             :          */
    3178           4 :         if (fbcon_has_exited && !(action == FB_EVENT_FB_REGISTERED ||
    3179             :                                   action == FB_EVENT_FB_UNREGISTERED))
    3180           1 :                 goto done;
    3181             : 
    3182           1 :         switch(action) {
    3183           4 :         case FB_EVENT_SUSPEND:
    3184           3 :                 fbcon_suspended(info);
    3185           1 :                 break;
    3186           4 :         case FB_EVENT_RESUME:
    3187           2 :                 fbcon_resumed(info);
    3188           1 :                 break;
    3189           4 :         case FB_EVENT_MODE_CHANGE:
    3190           5 :                 fbcon_modechanged(info);
    3191           1 :                 break;
    3192           4 :         case FB_EVENT_MODE_CHANGE_ALL:
    3193           4 :                 fbcon_set_all_vcs(info);
    3194           1 :                 break;
    3195           4 :         case FB_EVENT_MODE_DELETE:
    3196           2 :                 mode = event->data;
    3197           2 :                 ret = fbcon_mode_deleted(info, mode);
    3198           1 :                 break;
    3199           4 :         case FB_EVENT_FB_UNBIND:
    3200           1 :                 idx = info->node;
    3201           4 :                 ret = fbcon_fb_unbind(idx);
    3202           1 :                 break;
    3203           4 :         case FB_EVENT_FB_REGISTERED:
    3204           4 :                 ret = fbcon_fb_registered(info);
    3205           1 :                 break;
    3206           4 :         case FB_EVENT_FB_UNREGISTERED:
    3207           4 :                 ret = fbcon_fb_unregistered(info);
    3208           1 :                 break;
    3209           4 :         case FB_EVENT_SET_CONSOLE_MAP:
    3210           2 :                 con2fb = event->data;
    3211           5 :                 ret = set_con2fb_map(con2fb->console - 1,
    3212             :                                      con2fb->framebuffer, 1);
    3213           1 :                 break;
    3214           4 :         case FB_EVENT_GET_CONSOLE_MAP:
    3215           2 :                 con2fb = event->data;
    3216           1 :                 con2fb->framebuffer = con2fb_map[con2fb->console - 1];
    3217           1 :                 break;
    3218           4 :         case FB_EVENT_BLANK:
    3219           3 :                 fbcon_fb_blanked(info, *(int *)event->data);
    3220           1 :                 break;
    3221           4 :         case FB_EVENT_NEW_MODELIST:
    3222           2 :                 fbcon_new_modelist(info);
    3223           1 :                 break;
    3224           4 :         case FB_EVENT_GET_REQ:
    3225           2 :                 caps = event->data;
    3226           3 :                 fbcon_get_requirement(info, caps);
    3227           1 :                 break;
    3228           1 :         }
    3229             : done:
    3230          14 :         return ret;
    3231          13 : }
    3232             : 
    3233             : /*
    3234             :  *  The console `switch' structure for the frame buffer based console
    3235             :  */
    3236             : 
    3237           1 : static const struct consw fb_con = {
    3238             :         .owner                  = THIS_MODULE,
    3239             :         .con_startup            = fbcon_startup,
    3240             :         .con_init               = fbcon_init,
    3241             :         .con_deinit             = fbcon_deinit,
    3242             :         .con_clear              = fbcon_clear,
    3243             :         .con_putc               = fbcon_putc,
    3244             :         .con_putcs              = fbcon_putcs,
    3245             :         .con_cursor             = fbcon_cursor,
    3246             :         .con_scroll             = fbcon_scroll,
    3247             :         .con_bmove              = fbcon_bmove,
    3248             :         .con_switch             = fbcon_switch,
    3249             :         .con_blank              = fbcon_blank,
    3250             :         .con_font_set           = fbcon_set_font,
    3251             :         .con_font_get           = fbcon_get_font,
    3252             :         .con_font_default       = fbcon_set_def_font,
    3253             :         .con_font_copy          = fbcon_copy_font,
    3254             :         .con_set_palette        = fbcon_set_palette,
    3255             :         .con_scrolldelta        = fbcon_scrolldelta,
    3256             :         .con_set_origin         = fbcon_set_origin,
    3257             :         .con_invert_region      = fbcon_invert_region,
    3258             :         .con_screen_pos         = fbcon_screen_pos,
    3259             :         .con_getxy              = fbcon_getxy,
    3260             :         .con_resize             = fbcon_resize,
    3261             : };
    3262             : 
    3263           1 : static struct notifier_block fbcon_event_notifier = {
    3264             :         .notifier_call  = fbcon_event_notify,
    3265             : };
    3266             : 
    3267             : static ssize_t store_rotate(struct device *device,
    3268             :                             struct device_attribute *attr, const char *buf,
    3269             :                             size_t count)
    3270           1 : {
    3271           1 :         struct fb_info *info;
    3272           1 :         int rotate, idx;
    3273           2 :         char **last = NULL;
    3274           1 : 
    3275           2 :         if (fbcon_has_exited)
    3276           1 :                 return count;
    3277             : 
    3278           1 :         acquire_console_sem();
    3279           1 :         idx = con2fb_map[fg_console];
    3280             : 
    3281           4 :         if (idx == -1 || registered_fb[idx] == NULL)
    3282           1 :                 goto err;
    3283             : 
    3284           1 :         info = registered_fb[idx];
    3285           2 :         rotate = simple_strtoul(buf, last, 0);
    3286           2 :         fbcon_rotate(info, rotate);
    3287             : err:
    3288           3 :         release_console_sem();
    3289           2 :         return count;
    3290             : }
    3291             : 
    3292             : static ssize_t store_rotate_all(struct device *device,
    3293             :                                 struct device_attribute *attr,const char *buf,
    3294             :                                 size_t count)
    3295           1 : {
    3296           1 :         struct fb_info *info;
    3297           1 :         int rotate, idx;
    3298           2 :         char **last = NULL;
    3299           1 : 
    3300           2 :         if (fbcon_has_exited)
    3301           1 :                 return count;
    3302             : 
    3303           1 :         acquire_console_sem();
    3304           1 :         idx = con2fb_map[fg_console];
    3305             : 
    3306           4 :         if (idx == -1 || registered_fb[idx] == NULL)
    3307           1 :                 goto err;
    3308             : 
    3309           1 :         info = registered_fb[idx];
    3310           2 :         rotate = simple_strtoul(buf, last, 0);
    3311           2 :         fbcon_rotate_all(info, rotate);
    3312             : err:
    3313           3 :         release_console_sem();
    3314           2 :         return count;
    3315             : }
    3316             : 
    3317             : static ssize_t show_rotate(struct device *device,
    3318             :                            struct device_attribute *attr,char *buf)
    3319             : {
    3320           1 :         struct fb_info *info;
    3321           2 :         int rotate = 0, idx;
    3322           1 : 
    3323           3 :         if (fbcon_has_exited)
    3324           1 :                 return 0;
    3325             : 
    3326           1 :         acquire_console_sem();
    3327           1 :         idx = con2fb_map[fg_console];
    3328             : 
    3329           4 :         if (idx == -1 || registered_fb[idx] == NULL)
    3330           1 :                 goto err;
    3331             : 
    3332           1 :         info = registered_fb[idx];
    3333           2 :         rotate = fbcon_get_rotate(info);
    3334             : err:
    3335           3 :         release_console_sem();
    3336           4 :         return snprintf(buf, PAGE_SIZE, "%d\n", rotate);
    3337             : }
    3338             : 
    3339             : static ssize_t show_cursor_blink(struct device *device,
    3340             :                                  struct device_attribute *attr, char *buf)
    3341             : {
    3342           1 :         struct fb_info *info;
    3343           1 :         struct fbcon_ops *ops;
    3344           2 :         int idx, blink = -1;
    3345           1 : 
    3346           3 :         if (fbcon_has_exited)
    3347           1 :                 return 0;
    3348             : 
    3349           1 :         acquire_console_sem();
    3350           1 :         idx = con2fb_map[fg_console];
    3351             : 
    3352           4 :         if (idx == -1 || registered_fb[idx] == NULL)
    3353           1 :                 goto err;
    3354             : 
    3355           1 :         info = registered_fb[idx];
    3356           2 :         ops = info->fbcon_par;
    3357             : 
    3358           2 :         if (!ops)
    3359           1 :                 goto err;
    3360             : 
    3361           1 :         blink = (ops->flags & FBCON_FLAGS_CURSOR_TIMER) ? 1 : 0;
    3362           1 : err:
    3363           1 :         release_console_sem();
    3364           2 :         return snprintf(buf, PAGE_SIZE, "%d\n", blink);
    3365             : }
    3366             : 
    3367             : static ssize_t store_cursor_blink(struct device *device,
    3368             :                                   struct device_attribute *attr,
    3369             :                                   const char *buf, size_t count)
    3370           0 : {
    3371           0 :         struct fb_info *info;
    3372           0 :         int blink, idx;
    3373           0 :         char **last = NULL;
    3374           0 : 
    3375           0 :         if (fbcon_has_exited)
    3376           0 :                 return count;
    3377             : 
    3378           0 :         acquire_console_sem();
    3379           0 :         idx = con2fb_map[fg_console];
    3380             : 
    3381           0 :         if (idx == -1 || registered_fb[idx] == NULL)
    3382           0 :                 goto err;
    3383             : 
    3384           0 :         info = registered_fb[idx];
    3385             : 
    3386           0 :         if (!info->fbcon_par)
    3387           0 :                 goto err;
    3388             : 
    3389           0 :         blink = simple_strtoul(buf, last, 0);
    3390             : 
    3391           0 :         if (blink) {
    3392           0 :                 fbcon_cursor_noblink = 0;
    3393           0 :                 fbcon_add_cursor_timer(info);
    3394             :         } else {
    3395           0 :                 fbcon_cursor_noblink = 1;
    3396           0 :                 fbcon_del_cursor_timer(info);
    3397             :         }
    3398             : 
    3399           0 : err:
    3400           0 :         release_console_sem();
    3401           0 :         return count;
    3402             : }
    3403             : 
    3404           1 : static struct device_attribute device_attrs[] = {
    3405             :         __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
    3406             :         __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all),
    3407             :         __ATTR(cursor_blink, S_IRUGO|S_IWUSR, show_cursor_blink,
    3408             :                store_cursor_blink),
    3409             : };
    3410             : 
    3411             : static int fbcon_init_device(void)
    3412             : {
    3413           2 :         int i, error = 0;
    3414           1 : 
    3415           1 :         fbcon_has_sysfs = 1;
    3416             : 
    3417           5 :         for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
    3418           3 :                 error = device_create_file(fbcon_device, &device_attrs[i]);
    3419           1 : 
    3420           2 :                 if (error)
    3421           1 :                         break;
    3422             :         }
    3423           1 : 
    3424           2 :         if (error) {
    3425           4 :                 while (--i >= 0)
    3426           2 :                         device_remove_file(fbcon_device, &device_attrs[i]);
    3427           1 : 
    3428           2 :                 fbcon_has_sysfs = 0;
    3429             :         }
    3430             : 
    3431           2 :         return 0;
    3432             : }
    3433             : 
    3434             : static void fbcon_start(void)
    3435             : {
    3436           3 :         if (num_registered_fb) {
    3437             :                 int i;
    3438             : 
    3439           1 :                 acquire_console_sem();
    3440             : 
    3441           5 :                 for (i = 0; i < FB_MAX; i++) {
    3442           4 :                         if (registered_fb[i] != NULL) {
    3443           2 :                                 info_idx = i;
    3444           1 :                                 break;
    3445             :                         }
    3446             :                 }
    3447           1 : 
    3448           1 :                 release_console_sem();
    3449           4 :                 fbcon_takeover(0);
    3450             :         }
    3451           2 : }
    3452             : 
    3453             : static void fbcon_exit(void)
    3454             : {
    3455           2 :         struct fb_info *info;
    3456           2 :         int i, j, mapped;
    3457           2 : 
    3458           6 :         if (fbcon_has_exited)
    3459           4 :                 return;
    3460           2 : 
    3461           2 :         kfree((void *)softback_buf);
    3462           2 :         softback_buf = 0UL;
    3463             : 
    3464          16 :         for (i = 0; i < FB_MAX; i++) {
    3465          10 :                 int pending;
    3466           2 : 
    3467           2 :                 mapped = 0;
    3468           2 :                 info = registered_fb[i];
    3469             : 
    3470           4 :                 if (info == NULL)
    3471           2 :                         continue;
    3472             : 
    3473           2 :                 pending = cancel_work_sync(&info->queue);
    3474             :                 DPRINTK("fbcon: %s pending work\n", (pending ? "canceled" :
    3475             :                         "no"));
    3476             : 
    3477          10 :                 for (j = first_fb_vc; j <= last_fb_vc; j++) {
    3478           8 :                         if (con2fb_map[j] == i)
    3479           4 :                                 mapped = 1;
    3480             :                 }
    3481             : 
    3482           4 :                 if (mapped) {
    3483           6 :                         if (info->fbops->fb_release)
    3484           2 :                                 info->fbops->fb_release(info, 0);
    3485           4 :                         module_put(info->fbops->owner);
    3486             : 
    3487           6 :                         if (info->fbcon_par) {
    3488           4 :                                 struct fbcon_ops *ops = info->fbcon_par;
    3489             : 
    3490           6 :                                 fbcon_del_cursor_timer(info);
    3491           4 :                                 kfree(ops->cursor_src);
    3492           4 :                                 kfree(info->fbcon_par);
    3493           2 :                                 info->fbcon_par = NULL;
    3494             :                         }
    3495             : 
    3496           8 :                         if (info->queue.func == fb_flashcursor)
    3497           4 :                                 info->queue.func = NULL;
    3498             :                 }
    3499             :         }
    3500             : 
    3501           2 :         fbcon_has_exited = 1;
    3502           2 : }
    3503             : 
    3504           6 : static int __init fb_console_init(void)
    3505             : {
    3506           1 :         int i;
    3507           1 : 
    3508           2 :         acquire_console_sem();
    3509           2 :         fb_register_client(&fbcon_event_notifier);
    3510           1 :         fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
    3511             :                                      "fbcon");
    3512             : 
    3513           4 :         if (IS_ERR(fbcon_device)) {
    3514           3 :                 printk(KERN_WARNING "Unable to create device "
    3515             :                        "for fbcon; errno = %ld\n",
    3516             :                        PTR_ERR(fbcon_device));
    3517           1 :                 fbcon_device = NULL;
    3518             :         } else
    3519           3 :                 fbcon_init_device();
    3520             : 
    3521           7 :         for (i = 0; i < MAX_NR_CONSOLES; i++)
    3522           3 :                 con2fb_map[i] = -1;
    3523           1 : 
    3524           1 :         release_console_sem();
    3525           3 :         fbcon_start();
    3526           1 :         return 0;
    3527             : }
    3528             : 
    3529             : module_init(fb_console_init);
    3530           1 : 
    3531             : #ifdef MODULE
    3532             : 
    3533             : static void __exit fbcon_deinit_device(void)
    3534             : {
    3535             :         int i;
    3536             : 
    3537             :         if (fbcon_has_sysfs) {
    3538             :                 for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
    3539             :                         device_remove_file(fbcon_device, &device_attrs[i]);
    3540             : 
    3541             :                 fbcon_has_sysfs = 0;
    3542             :         }
    3543             : }
    3544             : 
    3545             : static void __exit fb_console_exit(void)
    3546             : {
    3547             :         acquire_console_sem();
    3548             :         fb_unregister_client(&fbcon_event_notifier);
    3549             :         fbcon_deinit_device();
    3550             :         device_destroy(fb_class, MKDEV(0, 0));
    3551             :         fbcon_exit();
    3552             :         release_console_sem();
    3553             :         unregister_con_driver(&fb_con);
    3554             : }       
    3555             : 
    3556             : module_exit(fb_console_exit);
    3557             : 
    3558             : #endif
    3559             : 
    3560             : MODULE_LICENSE("GPL");

Generated by: LCOV version 1.10