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