LCOV - code coverage report
Current view: top level - drivers/video/console - fbcon.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 435 1720 25.3 %
Date: 2015-04-12 14:34:49 Functions: 26 72 36.1 %

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

Generated by: LCOV version 1.11