root/drivers/video/fbdev/core/fbcon.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fbcon_map_override
  2. fbcon_map_override
  3. fbcon_set_rotation
  4. fbcon_rotate
  5. fbcon_rotate_all
  6. fbcon_set_rotation
  7. fbcon_rotate
  8. fbcon_rotate_all
  9. fbcon_get_rotate
  10. fbcon_is_inactive
  11. get_color
  12. fbcon_update_softback
  13. fb_flashcursor
  14. cursor_timer_handler
  15. fbcon_add_cursor_timer
  16. fbcon_del_cursor_timer
  17. fb_console_setup
  18. search_fb_in_map
  19. search_for_mapped_con
  20. do_fbcon_takeover
  21. fbcon_prepare_logo
  22. fbcon_prepare_logo
  23. set_blitting_type
  24. fbcon_invalid_charcount
  25. set_blitting_type
  26. fbcon_invalid_charcount
  27. con2fb_acquire_newinfo
  28. con2fb_release_oldinfo
  29. con2fb_init_display
  30. set_con2fb_map
  31. var_to_display
  32. display_to_var
  33. fbcon_startup
  34. fbcon_init
  35. fbcon_free_font
  36. fbcon_deinit
  37. fbcon_clear
  38. fbcon_putcs
  39. fbcon_putc
  40. fbcon_clear_margins
  41. fbcon_cursor
  42. fbcon_set_disp
  43. ywrap_up
  44. ywrap_down
  45. ypan_up
  46. ypan_up_redraw
  47. ypan_down
  48. ypan_down_redraw
  49. fbcon_redraw_softback
  50. fbcon_redraw_move
  51. fbcon_redraw_blit
  52. fbcon_redraw
  53. fbcon_softback_note
  54. fbcon_scroll
  55. fbcon_bmove
  56. fbcon_bmove_rec
  57. updatescrollmode
  58. fbcon_resize
  59. fbcon_switch
  60. fbcon_generic_blank
  61. fbcon_blank
  62. fbcon_debug_enter
  63. fbcon_debug_leave
  64. fbcon_get_font
  65. set_vc_hi_font
  66. fbcon_do_set_font
  67. fbcon_copy_font
  68. fbcon_set_font
  69. fbcon_set_def_font
  70. fbcon_set_palette
  71. fbcon_screen_pos
  72. fbcon_getxy
  73. fbcon_invert_region
  74. fbcon_scrolldelta
  75. fbcon_set_origin
  76. fbcon_suspended
  77. fbcon_resumed
  78. fbcon_modechanged
  79. fbcon_set_all_vcs
  80. fbcon_update_vcs
  81. fbcon_mode_deleted
  82. fbcon_unbind
  83. fbcon_unbind
  84. fbcon_fb_unbind
  85. fbcon_fb_unregistered
  86. fbcon_remap_all
  87. fbcon_select_primary
  88. fbcon_select_primary
  89. fbcon_fb_registered
  90. fbcon_fb_blanked
  91. fbcon_new_modelist
  92. fbcon_get_requirement
  93. fbcon_set_con2fb_map_ioctl
  94. fbcon_get_con2fb_map_ioctl
  95. store_rotate
  96. store_rotate_all
  97. show_rotate
  98. show_cursor_blink
  99. store_cursor_blink
  100. fbcon_init_device
  101. fbcon_register_existing_fbs
  102. fbcon_output_notifier
  103. fbcon_start
  104. fbcon_exit
  105. fb_console_init
  106. fbcon_deinit_device
  107. fb_console_exit

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

/* [<][>][^][v][top][bottom][index][help] */