0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059 #include <linux/module.h>
0060 #include <linux/types.h>
0061 #include <linux/fs.h>
0062 #include <linux/kernel.h>
0063 #include <linux/delay.h> /* MSch: for IRQ probe */
0064 #include <linux/console.h>
0065 #include <linux/string.h>
0066 #include <linux/kd.h>
0067 #include <linux/slab.h>
0068 #include <linux/fb.h>
0069 #include <linux/fbcon.h>
0070 #include <linux/vt_kern.h>
0071 #include <linux/selection.h>
0072 #include <linux/font.h>
0073 #include <linux/smp.h>
0074 #include <linux/init.h>
0075 #include <linux/interrupt.h>
0076 #include <linux/crc32.h> /* For counting font checksums */
0077 #include <linux/uaccess.h>
0078 #include <asm/fb.h>
0079 #include <asm/irq.h>
0080
0081 #include "fbcon.h"
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098 enum {
0099 FBCON_LOGO_CANSHOW = -1,
0100 FBCON_LOGO_DRAW = -2,
0101 FBCON_LOGO_DONTSHOW = -3
0102 };
0103
0104 static struct fbcon_display fb_display[MAX_NR_CONSOLES];
0105
0106 struct fb_info *fbcon_registered_fb[FB_MAX];
0107 int fbcon_num_registered_fb;
0108
0109 #define fbcon_for_each_registered_fb(i) \
0110 for (i = 0; WARN_CONSOLE_UNLOCKED(), i < FB_MAX; i++) \
0111 if (!fbcon_registered_fb[i]) {} else
0112
0113 static signed char con2fb_map[MAX_NR_CONSOLES];
0114 static signed char con2fb_map_boot[MAX_NR_CONSOLES];
0115
0116 static struct fb_info *fbcon_info_from_console(int console)
0117 {
0118 WARN_CONSOLE_UNLOCKED();
0119
0120 return fbcon_registered_fb[con2fb_map[console]];
0121 }
0122
0123 static int logo_lines;
0124
0125
0126 static int logo_shown = FBCON_LOGO_CANSHOW;
0127
0128 static unsigned int first_fb_vc;
0129 static unsigned int last_fb_vc = MAX_NR_CONSOLES - 1;
0130 static int fbcon_is_default = 1;
0131 static int primary_device = -1;
0132 static int fbcon_has_console_bind;
0133
0134 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
0135 static int map_override;
0136
0137 static inline void fbcon_map_override(void)
0138 {
0139 map_override = 1;
0140 }
0141 #else
0142 static inline void fbcon_map_override(void)
0143 {
0144 }
0145 #endif
0146
0147 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
0148 static bool deferred_takeover = true;
0149 #else
0150 #define deferred_takeover false
0151 #endif
0152
0153
0154 static char fontname[40];
0155
0156
0157 static int info_idx = -1;
0158
0159
0160 static int initial_rotation = -1;
0161 static int fbcon_has_sysfs;
0162 static int margin_color;
0163
0164 static const struct consw fb_con;
0165
0166 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
0167
0168 static int fbcon_cursor_noblink;
0169
0170 #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1)
0171
0172
0173
0174
0175
0176 static void fbcon_clear_margins(struct vc_data *vc, int bottom_only);
0177 static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table);
0178
0179
0180
0181
0182 static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
0183 int unit);
0184 static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
0185 int line, int count, int dy);
0186 static void fbcon_modechanged(struct fb_info *info);
0187 static void fbcon_set_all_vcs(struct fb_info *info);
0188
0189 static struct device *fbcon_device;
0190
0191 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
0192 static inline void fbcon_set_rotation(struct fb_info *info)
0193 {
0194 struct fbcon_ops *ops = info->fbcon_par;
0195
0196 if (!(info->flags & FBINFO_MISC_TILEBLITTING) &&
0197 ops->p->con_rotate < 4)
0198 ops->rotate = ops->p->con_rotate;
0199 else
0200 ops->rotate = 0;
0201 }
0202
0203 static void fbcon_rotate(struct fb_info *info, u32 rotate)
0204 {
0205 struct fbcon_ops *ops= info->fbcon_par;
0206 struct fb_info *fb_info;
0207
0208 if (!ops || ops->currcon == -1)
0209 return;
0210
0211 fb_info = fbcon_info_from_console(ops->currcon);
0212
0213 if (info == fb_info) {
0214 struct fbcon_display *p = &fb_display[ops->currcon];
0215
0216 if (rotate < 4)
0217 p->con_rotate = rotate;
0218 else
0219 p->con_rotate = 0;
0220
0221 fbcon_modechanged(info);
0222 }
0223 }
0224
0225 static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
0226 {
0227 struct fbcon_ops *ops = info->fbcon_par;
0228 struct vc_data *vc;
0229 struct fbcon_display *p;
0230 int i;
0231
0232 if (!ops || ops->currcon < 0 || rotate > 3)
0233 return;
0234
0235 for (i = first_fb_vc; i <= last_fb_vc; i++) {
0236 vc = vc_cons[i].d;
0237 if (!vc || vc->vc_mode != KD_TEXT ||
0238 fbcon_info_from_console(i) != info)
0239 continue;
0240
0241 p = &fb_display[vc->vc_num];
0242 p->con_rotate = rotate;
0243 }
0244
0245 fbcon_set_all_vcs(info);
0246 }
0247 #else
0248 static inline void fbcon_set_rotation(struct fb_info *info)
0249 {
0250 struct fbcon_ops *ops = info->fbcon_par;
0251
0252 ops->rotate = FB_ROTATE_UR;
0253 }
0254
0255 static void fbcon_rotate(struct fb_info *info, u32 rotate)
0256 {
0257 return;
0258 }
0259
0260 static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
0261 {
0262 return;
0263 }
0264 #endif
0265
0266 static int fbcon_get_rotate(struct fb_info *info)
0267 {
0268 struct fbcon_ops *ops = info->fbcon_par;
0269
0270 return (ops) ? ops->rotate : 0;
0271 }
0272
0273 static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
0274 {
0275 struct fbcon_ops *ops = info->fbcon_par;
0276
0277 return (info->state != FBINFO_STATE_RUNNING ||
0278 vc->vc_mode != KD_TEXT || ops->graphics);
0279 }
0280
0281 static int get_color(struct vc_data *vc, struct fb_info *info,
0282 u16 c, int is_fg)
0283 {
0284 int depth = fb_get_color_depth(&info->var, &info->fix);
0285 int color = 0;
0286
0287 if (console_blanked) {
0288 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
0289
0290 c = vc->vc_video_erase_char & charmask;
0291 }
0292
0293 if (depth != 1)
0294 color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c)
0295 : attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c);
0296
0297 switch (depth) {
0298 case 1:
0299 {
0300 int col = mono_col(info);
0301
0302 int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0;
0303 int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col;
0304
0305 if (console_blanked)
0306 fg = bg;
0307
0308 color = (is_fg) ? fg : bg;
0309 break;
0310 }
0311 case 2:
0312
0313
0314
0315
0316
0317
0318
0319 switch (color) {
0320 case 0:
0321 color = 0;
0322 break;
0323 case 1 ... 6:
0324 color = 2;
0325 break;
0326 case 7 ... 8:
0327 color = 1;
0328 break;
0329 default:
0330 color = 3;
0331 break;
0332 }
0333 break;
0334 case 3:
0335
0336
0337
0338
0339
0340 color &= 7;
0341 break;
0342 }
0343
0344
0345 return color;
0346 }
0347
0348 static void fb_flashcursor(struct work_struct *work)
0349 {
0350 struct fbcon_ops *ops = container_of(work, struct fbcon_ops, cursor_work.work);
0351 struct fb_info *info;
0352 struct vc_data *vc = NULL;
0353 int c;
0354 int mode;
0355 int ret;
0356
0357
0358
0359
0360 ret = console_trylock();
0361 if (ret == 0)
0362 return;
0363
0364
0365 info = ops->info;
0366
0367 if (ops->currcon != -1)
0368 vc = vc_cons[ops->currcon].d;
0369
0370 if (!vc || !con_is_visible(vc) ||
0371 fbcon_info_from_console(vc->vc_num) != info ||
0372 vc->vc_deccm != 1) {
0373 console_unlock();
0374 return;
0375 }
0376
0377 c = scr_readw((u16 *) vc->vc_pos);
0378 mode = (!ops->cursor_flash || ops->cursor_state.enable) ?
0379 CM_ERASE : CM_DRAW;
0380 ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
0381 get_color(vc, info, c, 0));
0382 console_unlock();
0383
0384 queue_delayed_work(system_power_efficient_wq, &ops->cursor_work,
0385 ops->cur_blink_jiffies);
0386 }
0387
0388 static void fbcon_add_cursor_work(struct fb_info *info)
0389 {
0390 struct fbcon_ops *ops = info->fbcon_par;
0391
0392 if (!fbcon_cursor_noblink)
0393 queue_delayed_work(system_power_efficient_wq, &ops->cursor_work,
0394 ops->cur_blink_jiffies);
0395 }
0396
0397 static void fbcon_del_cursor_work(struct fb_info *info)
0398 {
0399 struct fbcon_ops *ops = info->fbcon_par;
0400
0401 cancel_delayed_work_sync(&ops->cursor_work);
0402 }
0403
0404 #ifndef MODULE
0405 static int __init fb_console_setup(char *this_opt)
0406 {
0407 char *options;
0408 int i, j;
0409
0410 if (!this_opt || !*this_opt)
0411 return 1;
0412
0413 while ((options = strsep(&this_opt, ",")) != NULL) {
0414 if (!strncmp(options, "font:", 5)) {
0415 strscpy(fontname, options + 5, sizeof(fontname));
0416 continue;
0417 }
0418
0419 if (!strncmp(options, "scrollback:", 11)) {
0420 pr_warn("Ignoring scrollback size option\n");
0421 continue;
0422 }
0423
0424 if (!strncmp(options, "map:", 4)) {
0425 options += 4;
0426 if (*options) {
0427 for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) {
0428 if (!options[j])
0429 j = 0;
0430 con2fb_map_boot[i] =
0431 (options[j++]-'0') % FB_MAX;
0432 }
0433
0434 fbcon_map_override();
0435 }
0436 continue;
0437 }
0438
0439 if (!strncmp(options, "vc:", 3)) {
0440 options += 3;
0441 if (*options)
0442 first_fb_vc = simple_strtoul(options, &options, 10) - 1;
0443 if (first_fb_vc >= MAX_NR_CONSOLES)
0444 first_fb_vc = 0;
0445 if (*options++ == '-')
0446 last_fb_vc = simple_strtoul(options, &options, 10) - 1;
0447 if (last_fb_vc < first_fb_vc || last_fb_vc >= MAX_NR_CONSOLES)
0448 last_fb_vc = MAX_NR_CONSOLES - 1;
0449 fbcon_is_default = 0;
0450 continue;
0451 }
0452
0453 if (!strncmp(options, "rotate:", 7)) {
0454 options += 7;
0455 if (*options)
0456 initial_rotation = simple_strtoul(options, &options, 0);
0457 if (initial_rotation > 3)
0458 initial_rotation = 0;
0459 continue;
0460 }
0461
0462 if (!strncmp(options, "margin:", 7)) {
0463 options += 7;
0464 if (*options)
0465 margin_color = simple_strtoul(options, &options, 0);
0466 continue;
0467 }
0468 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
0469 if (!strcmp(options, "nodefer")) {
0470 deferred_takeover = false;
0471 continue;
0472 }
0473 #endif
0474
0475 if (!strncmp(options, "logo-pos:", 9)) {
0476 options += 9;
0477 if (!strcmp(options, "center"))
0478 fb_center_logo = true;
0479 continue;
0480 }
0481
0482 if (!strncmp(options, "logo-count:", 11)) {
0483 options += 11;
0484 if (*options)
0485 fb_logo_count = simple_strtol(options, &options, 0);
0486 continue;
0487 }
0488 }
0489 return 1;
0490 }
0491
0492 __setup("fbcon=", fb_console_setup);
0493 #endif
0494
0495 static int search_fb_in_map(int idx)
0496 {
0497 int i, retval = 0;
0498
0499 for (i = first_fb_vc; i <= last_fb_vc; i++) {
0500 if (con2fb_map[i] == idx)
0501 retval = 1;
0502 }
0503 return retval;
0504 }
0505
0506 static int search_for_mapped_con(void)
0507 {
0508 int i, retval = 0;
0509
0510 for (i = first_fb_vc; i <= last_fb_vc; i++) {
0511 if (con2fb_map[i] != -1)
0512 retval = 1;
0513 }
0514 return retval;
0515 }
0516
0517 static int do_fbcon_takeover(int show_logo)
0518 {
0519 int err, i;
0520
0521 if (!fbcon_num_registered_fb)
0522 return -ENODEV;
0523
0524 if (!show_logo)
0525 logo_shown = FBCON_LOGO_DONTSHOW;
0526
0527 for (i = first_fb_vc; i <= last_fb_vc; i++)
0528 con2fb_map[i] = info_idx;
0529
0530 err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc,
0531 fbcon_is_default);
0532
0533 if (err) {
0534 for (i = first_fb_vc; i <= last_fb_vc; i++)
0535 con2fb_map[i] = -1;
0536 info_idx = -1;
0537 } else {
0538 fbcon_has_console_bind = 1;
0539 }
0540
0541 return err;
0542 }
0543
0544 #ifdef MODULE
0545 static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
0546 int cols, int rows, int new_cols, int new_rows)
0547 {
0548 logo_shown = FBCON_LOGO_DONTSHOW;
0549 }
0550 #else
0551 static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
0552 int cols, int rows, int new_cols, int new_rows)
0553 {
0554
0555 struct fbcon_ops *ops = info->fbcon_par;
0556 int cnt, erase = vc->vc_video_erase_char, step;
0557 unsigned short *save = NULL, *r, *q;
0558 int logo_height;
0559
0560 if (info->fbops->owner) {
0561 logo_shown = FBCON_LOGO_DONTSHOW;
0562 return;
0563 }
0564
0565
0566
0567
0568
0569 if (fb_get_color_depth(&info->var, &info->fix) == 1)
0570 erase &= ~0x400;
0571 logo_height = fb_prepare_logo(info, ops->rotate);
0572 logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height);
0573 q = (unsigned short *) (vc->vc_origin +
0574 vc->vc_size_row * rows);
0575 step = logo_lines * cols;
0576 for (r = q - logo_lines * cols; r < q; r++)
0577 if (scr_readw(r) != vc->vc_video_erase_char)
0578 break;
0579 if (r != q && new_rows >= rows + logo_lines) {
0580 save = kmalloc(array3_size(logo_lines, new_cols, 2),
0581 GFP_KERNEL);
0582 if (save) {
0583 int i = min(cols, new_cols);
0584 scr_memsetw(save, erase, array3_size(logo_lines, new_cols, 2));
0585 r = q - step;
0586 for (cnt = 0; cnt < logo_lines; cnt++, r += i)
0587 scr_memcpyw(save + cnt * new_cols, r, 2 * i);
0588 r = q;
0589 }
0590 }
0591 if (r == q) {
0592
0593 r = q - step - cols;
0594 for (cnt = rows - logo_lines; cnt > 0; cnt--) {
0595 scr_memcpyw(r + step, r, vc->vc_size_row);
0596 r -= cols;
0597 }
0598 if (!save) {
0599 int lines;
0600 if (vc->state.y + logo_lines >= rows)
0601 lines = rows - vc->state.y - 1;
0602 else
0603 lines = logo_lines;
0604 vc->state.y += lines;
0605 vc->vc_pos += lines * vc->vc_size_row;
0606 }
0607 }
0608 scr_memsetw((unsigned short *) vc->vc_origin,
0609 erase,
0610 vc->vc_size_row * logo_lines);
0611
0612 if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
0613 fbcon_clear_margins(vc, 0);
0614 update_screen(vc);
0615 }
0616
0617 if (save) {
0618 q = (unsigned short *) (vc->vc_origin +
0619 vc->vc_size_row *
0620 rows);
0621 scr_memcpyw(q, save, array3_size(logo_lines, new_cols, 2));
0622 vc->state.y += logo_lines;
0623 vc->vc_pos += logo_lines * vc->vc_size_row;
0624 kfree(save);
0625 }
0626
0627 if (logo_shown == FBCON_LOGO_DONTSHOW)
0628 return;
0629
0630 if (logo_lines > vc->vc_bottom) {
0631 logo_shown = FBCON_LOGO_CANSHOW;
0632 printk(KERN_INFO
0633 "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
0634 } else {
0635 logo_shown = FBCON_LOGO_DRAW;
0636 vc->vc_top = logo_lines;
0637 }
0638 }
0639 #endif
0640
0641 #ifdef CONFIG_FB_TILEBLITTING
0642 static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
0643 {
0644 struct fbcon_ops *ops = info->fbcon_par;
0645
0646 ops->p = &fb_display[vc->vc_num];
0647
0648 if ((info->flags & FBINFO_MISC_TILEBLITTING))
0649 fbcon_set_tileops(vc, info);
0650 else {
0651 fbcon_set_rotation(info);
0652 fbcon_set_bitops(ops);
0653 }
0654 }
0655
0656 static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
0657 {
0658 int err = 0;
0659
0660 if (info->flags & FBINFO_MISC_TILEBLITTING &&
0661 info->tileops->fb_get_tilemax(info) < charcount)
0662 err = 1;
0663
0664 return err;
0665 }
0666 #else
0667 static void set_blitting_type(struct vc_data *vc, struct fb_info *info)
0668 {
0669 struct fbcon_ops *ops = info->fbcon_par;
0670
0671 info->flags &= ~FBINFO_MISC_TILEBLITTING;
0672 ops->p = &fb_display[vc->vc_num];
0673 fbcon_set_rotation(info);
0674 fbcon_set_bitops(ops);
0675 }
0676
0677 static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
0678 {
0679 return 0;
0680 }
0681
0682 #endif
0683
0684 static void fbcon_release(struct fb_info *info)
0685 {
0686 lock_fb_info(info);
0687 if (info->fbops->fb_release)
0688 info->fbops->fb_release(info, 0);
0689 unlock_fb_info(info);
0690
0691 module_put(info->fbops->owner);
0692
0693 if (info->fbcon_par) {
0694 struct fbcon_ops *ops = info->fbcon_par;
0695
0696 fbcon_del_cursor_work(info);
0697 kfree(ops->cursor_state.mask);
0698 kfree(ops->cursor_data);
0699 kfree(ops->cursor_src);
0700 kfree(ops->fontbuffer);
0701 kfree(info->fbcon_par);
0702 info->fbcon_par = NULL;
0703 }
0704 }
0705
0706 static int fbcon_open(struct fb_info *info)
0707 {
0708 struct fbcon_ops *ops;
0709
0710 if (!try_module_get(info->fbops->owner))
0711 return -ENODEV;
0712
0713 lock_fb_info(info);
0714 if (info->fbops->fb_open &&
0715 info->fbops->fb_open(info, 0)) {
0716 unlock_fb_info(info);
0717 module_put(info->fbops->owner);
0718 return -ENODEV;
0719 }
0720 unlock_fb_info(info);
0721
0722 ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL);
0723 if (!ops) {
0724 fbcon_release(info);
0725 return -ENOMEM;
0726 }
0727
0728 INIT_DELAYED_WORK(&ops->cursor_work, fb_flashcursor);
0729 ops->info = info;
0730 info->fbcon_par = ops;
0731 ops->cur_blink_jiffies = HZ / 5;
0732
0733 return 0;
0734 }
0735
0736 static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info,
0737 int unit)
0738 {
0739 int err;
0740
0741 err = fbcon_open(info);
0742 if (err)
0743 return err;
0744
0745 if (vc)
0746 set_blitting_type(vc, info);
0747
0748 return err;
0749 }
0750
0751 static void con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,
0752 struct fb_info *newinfo)
0753 {
0754 int ret;
0755
0756 fbcon_release(oldinfo);
0757
0758
0759
0760
0761
0762
0763
0764
0765 if (newinfo && newinfo->fbops->fb_set_par) {
0766 ret = newinfo->fbops->fb_set_par(newinfo);
0767
0768 if (ret)
0769 printk(KERN_ERR "con2fb_release_oldinfo: "
0770 "detected unhandled fb_set_par error, "
0771 "error code %d\n", ret);
0772 }
0773 }
0774
0775 static void con2fb_init_display(struct vc_data *vc, struct fb_info *info,
0776 int unit, int show_logo)
0777 {
0778 struct fbcon_ops *ops = info->fbcon_par;
0779 int ret;
0780
0781 ops->currcon = fg_console;
0782
0783 if (info->fbops->fb_set_par && !ops->initialized) {
0784 ret = info->fbops->fb_set_par(info);
0785
0786 if (ret)
0787 printk(KERN_ERR "con2fb_init_display: detected "
0788 "unhandled fb_set_par error, "
0789 "error code %d\n", ret);
0790 }
0791
0792 ops->initialized = true;
0793 ops->graphics = 0;
0794 fbcon_set_disp(info, &info->var, unit);
0795
0796 if (show_logo) {
0797 struct vc_data *fg_vc = vc_cons[fg_console].d;
0798 struct fb_info *fg_info =
0799 fbcon_info_from_console(fg_console);
0800
0801 fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols,
0802 fg_vc->vc_rows, fg_vc->vc_cols,
0803 fg_vc->vc_rows);
0804 }
0805
0806 update_screen(vc_cons[fg_console].d);
0807 }
0808
0809
0810
0811
0812
0813
0814
0815
0816
0817
0818
0819
0820 static int set_con2fb_map(int unit, int newidx, int user)
0821 {
0822 struct vc_data *vc = vc_cons[unit].d;
0823 int oldidx = con2fb_map[unit];
0824 struct fb_info *info = fbcon_registered_fb[newidx];
0825 struct fb_info *oldinfo = NULL;
0826 int found, err = 0, show_logo;
0827
0828 WARN_CONSOLE_UNLOCKED();
0829
0830 if (oldidx == newidx)
0831 return 0;
0832
0833 if (!info)
0834 return -EINVAL;
0835
0836 if (!search_for_mapped_con() || !con_is_bound(&fb_con)) {
0837 info_idx = newidx;
0838 return do_fbcon_takeover(0);
0839 }
0840
0841 if (oldidx != -1)
0842 oldinfo = fbcon_registered_fb[oldidx];
0843
0844 found = search_fb_in_map(newidx);
0845
0846 if (!err && !found) {
0847 err = con2fb_acquire_newinfo(vc, info, unit);
0848 if (!err)
0849 con2fb_map[unit] = newidx;
0850 }
0851
0852
0853
0854
0855
0856 if (!err && oldinfo && !search_fb_in_map(oldidx))
0857 con2fb_release_oldinfo(vc, oldinfo, info);
0858
0859 show_logo = (fg_console == 0 && !user &&
0860 logo_shown != FBCON_LOGO_DONTSHOW);
0861
0862 if (!found)
0863 fbcon_add_cursor_work(info);
0864 con2fb_map_boot[unit] = newidx;
0865 con2fb_init_display(vc, info, unit, show_logo);
0866
0867 if (!search_fb_in_map(info_idx))
0868 info_idx = newidx;
0869
0870 return err;
0871 }
0872
0873
0874
0875
0876
0877 static int var_to_display(struct fbcon_display *disp,
0878 struct fb_var_screeninfo *var,
0879 struct fb_info *info)
0880 {
0881 disp->xres_virtual = var->xres_virtual;
0882 disp->yres_virtual = var->yres_virtual;
0883 disp->bits_per_pixel = var->bits_per_pixel;
0884 disp->grayscale = var->grayscale;
0885 disp->nonstd = var->nonstd;
0886 disp->accel_flags = var->accel_flags;
0887 disp->height = var->height;
0888 disp->width = var->width;
0889 disp->red = var->red;
0890 disp->green = var->green;
0891 disp->blue = var->blue;
0892 disp->transp = var->transp;
0893 disp->rotate = var->rotate;
0894 disp->mode = fb_match_mode(var, &info->modelist);
0895 if (disp->mode == NULL)
0896
0897 return -EINVAL;
0898 return 0;
0899 }
0900
0901 static void display_to_var(struct fb_var_screeninfo *var,
0902 struct fbcon_display *disp)
0903 {
0904 fb_videomode_to_var(var, disp->mode);
0905 var->xres_virtual = disp->xres_virtual;
0906 var->yres_virtual = disp->yres_virtual;
0907 var->bits_per_pixel = disp->bits_per_pixel;
0908 var->grayscale = disp->grayscale;
0909 var->nonstd = disp->nonstd;
0910 var->accel_flags = disp->accel_flags;
0911 var->height = disp->height;
0912 var->width = disp->width;
0913 var->red = disp->red;
0914 var->green = disp->green;
0915 var->blue = disp->blue;
0916 var->transp = disp->transp;
0917 var->rotate = disp->rotate;
0918 }
0919
0920 static const char *fbcon_startup(void)
0921 {
0922 const char *display_desc = "frame buffer device";
0923 struct fbcon_display *p = &fb_display[fg_console];
0924 struct vc_data *vc = vc_cons[fg_console].d;
0925 const struct font_desc *font = NULL;
0926 struct fb_info *info = NULL;
0927 struct fbcon_ops *ops;
0928 int rows, cols;
0929
0930
0931
0932
0933
0934 if (!fbcon_num_registered_fb || info_idx == -1)
0935 return display_desc;
0936
0937
0938
0939
0940 info = fbcon_registered_fb[info_idx];
0941 if (!info)
0942 return NULL;
0943
0944 if (fbcon_open(info))
0945 return NULL;
0946
0947 ops = info->fbcon_par;
0948 ops->currcon = -1;
0949 ops->graphics = 1;
0950 ops->cur_rotate = -1;
0951
0952 p->con_rotate = initial_rotation;
0953 if (p->con_rotate == -1)
0954 p->con_rotate = info->fbcon_rotate_hint;
0955 if (p->con_rotate == -1)
0956 p->con_rotate = FB_ROTATE_UR;
0957
0958 set_blitting_type(vc, info);
0959
0960
0961 if (!p->fontdata && !vc->vc_font.data) {
0962 if (!fontname[0] || !(font = find_font(fontname)))
0963 font = get_default_font(info->var.xres,
0964 info->var.yres,
0965 info->pixmap.blit_x,
0966 info->pixmap.blit_y);
0967 vc->vc_font.width = font->width;
0968 vc->vc_font.height = font->height;
0969 vc->vc_font.data = (void *)(p->fontdata = font->data);
0970 vc->vc_font.charcount = font->charcount;
0971 } else {
0972 p->fontdata = vc->vc_font.data;
0973 }
0974
0975 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
0976 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
0977 cols /= vc->vc_font.width;
0978 rows /= vc->vc_font.height;
0979 vc_resize(vc, cols, rows);
0980
0981 pr_debug("mode: %s\n", info->fix.id);
0982 pr_debug("visual: %d\n", info->fix.visual);
0983 pr_debug("res: %dx%d-%d\n", info->var.xres,
0984 info->var.yres,
0985 info->var.bits_per_pixel);
0986
0987 fbcon_add_cursor_work(info);
0988 return display_desc;
0989 }
0990
0991 static void fbcon_init(struct vc_data *vc, int init)
0992 {
0993 struct fb_info *info;
0994 struct fbcon_ops *ops;
0995 struct vc_data **default_mode = vc->vc_display_fg;
0996 struct vc_data *svc = *default_mode;
0997 struct fbcon_display *t, *p = &fb_display[vc->vc_num];
0998 int logo = 1, new_rows, new_cols, rows, cols;
0999 int ret;
1000
1001 if (WARN_ON(info_idx == -1))
1002 return;
1003
1004 if (con2fb_map[vc->vc_num] == -1)
1005 con2fb_map[vc->vc_num] = info_idx;
1006
1007 info = fbcon_info_from_console(vc->vc_num);
1008
1009 if (logo_shown < 0 && console_loglevel <= CONSOLE_LOGLEVEL_QUIET)
1010 logo_shown = FBCON_LOGO_DONTSHOW;
1011
1012 if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW ||
1013 (info->fix.type == FB_TYPE_TEXT))
1014 logo = 0;
1015
1016 if (var_to_display(p, &info->var, info))
1017 return;
1018
1019 if (!info->fbcon_par)
1020 con2fb_acquire_newinfo(vc, info, vc->vc_num);
1021
1022
1023
1024 t = &fb_display[fg_console];
1025 if (!p->fontdata) {
1026 if (t->fontdata) {
1027 struct vc_data *fvc = vc_cons[fg_console].d;
1028
1029 vc->vc_font.data = (void *)(p->fontdata =
1030 fvc->vc_font.data);
1031 vc->vc_font.width = fvc->vc_font.width;
1032 vc->vc_font.height = fvc->vc_font.height;
1033 vc->vc_font.charcount = fvc->vc_font.charcount;
1034 p->userfont = t->userfont;
1035
1036 if (p->userfont)
1037 REFCOUNT(p->fontdata)++;
1038 } else {
1039 const struct font_desc *font = NULL;
1040
1041 if (!fontname[0] || !(font = find_font(fontname)))
1042 font = get_default_font(info->var.xres,
1043 info->var.yres,
1044 info->pixmap.blit_x,
1045 info->pixmap.blit_y);
1046 vc->vc_font.width = font->width;
1047 vc->vc_font.height = font->height;
1048 vc->vc_font.data = (void *)(p->fontdata = font->data);
1049 vc->vc_font.charcount = font->charcount;
1050 }
1051 }
1052
1053 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1054 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1055 if (vc->vc_font.charcount == 256) {
1056 vc->vc_hi_font_mask = 0;
1057 } else {
1058 vc->vc_hi_font_mask = 0x100;
1059 if (vc->vc_can_do_color)
1060 vc->vc_complement_mask <<= 1;
1061 }
1062
1063 if (!*svc->uni_pagedict_loc)
1064 con_set_default_unimap(svc);
1065 if (!*vc->uni_pagedict_loc)
1066 con_copy_unimap(vc, svc);
1067
1068 ops = info->fbcon_par;
1069 ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
1070
1071 p->con_rotate = initial_rotation;
1072 if (p->con_rotate == -1)
1073 p->con_rotate = info->fbcon_rotate_hint;
1074 if (p->con_rotate == -1)
1075 p->con_rotate = FB_ROTATE_UR;
1076
1077 set_blitting_type(vc, info);
1078
1079 cols = vc->vc_cols;
1080 rows = vc->vc_rows;
1081 new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1082 new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1083 new_cols /= vc->vc_font.width;
1084 new_rows /= vc->vc_font.height;
1085
1086
1087
1088
1089
1090
1091
1092
1093 if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
1094 if (info->fbops->fb_set_par && !ops->initialized) {
1095 ret = info->fbops->fb_set_par(info);
1096
1097 if (ret)
1098 printk(KERN_ERR "fbcon_init: detected "
1099 "unhandled fb_set_par error, "
1100 "error code %d\n", ret);
1101 }
1102
1103 ops->initialized = true;
1104 }
1105
1106 ops->graphics = 0;
1107
1108 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
1109 if ((info->flags & FBINFO_HWACCEL_COPYAREA) &&
1110 !(info->flags & FBINFO_HWACCEL_DISABLED))
1111 p->scrollmode = SCROLL_MOVE;
1112 else
1113 p->scrollmode = SCROLL_REDRAW;
1114 #endif
1115
1116
1117
1118
1119
1120
1121 if (init) {
1122 vc->vc_cols = new_cols;
1123 vc->vc_rows = new_rows;
1124 } else
1125 vc_resize(vc, new_cols, new_rows);
1126
1127 if (logo)
1128 fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
1129
1130 if (ops->rotate_font && ops->rotate_font(info, vc)) {
1131 ops->rotate = FB_ROTATE_UR;
1132 set_blitting_type(vc, info);
1133 }
1134
1135 ops->p = &fb_display[fg_console];
1136 }
1137
1138 static void fbcon_free_font(struct fbcon_display *p, bool freefont)
1139 {
1140 if (freefont && p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
1141 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
1142 p->fontdata = NULL;
1143 p->userfont = 0;
1144 }
1145
1146 static void set_vc_hi_font(struct vc_data *vc, bool set);
1147
1148 static void fbcon_release_all(void)
1149 {
1150 struct fb_info *info;
1151 int i, j, mapped;
1152
1153 fbcon_for_each_registered_fb(i) {
1154 mapped = 0;
1155 info = fbcon_registered_fb[i];
1156
1157 for (j = first_fb_vc; j <= last_fb_vc; j++) {
1158 if (con2fb_map[j] == i) {
1159 mapped = 1;
1160 con2fb_map[j] = -1;
1161 }
1162 }
1163
1164 if (mapped)
1165 fbcon_release(info);
1166 }
1167 }
1168
1169 static void fbcon_deinit(struct vc_data *vc)
1170 {
1171 struct fbcon_display *p = &fb_display[vc->vc_num];
1172 struct fb_info *info;
1173 struct fbcon_ops *ops;
1174 int idx;
1175 bool free_font = true;
1176
1177 idx = con2fb_map[vc->vc_num];
1178
1179 if (idx == -1)
1180 goto finished;
1181
1182 info = fbcon_registered_fb[idx];
1183
1184 if (!info)
1185 goto finished;
1186
1187 if (info->flags & FBINFO_MISC_FIRMWARE)
1188 free_font = false;
1189 ops = info->fbcon_par;
1190
1191 if (!ops)
1192 goto finished;
1193
1194 if (con_is_visible(vc))
1195 fbcon_del_cursor_work(info);
1196
1197 ops->initialized = false;
1198 finished:
1199
1200 fbcon_free_font(p, free_font);
1201 if (free_font)
1202 vc->vc_font.data = NULL;
1203
1204 if (vc->vc_hi_font_mask && vc->vc_screenbuf)
1205 set_vc_hi_font(vc, false);
1206
1207 if (!con_is_bound(&fb_con))
1208 fbcon_release_all();
1209
1210 if (vc->vc_num == logo_shown)
1211 logo_shown = FBCON_LOGO_CANSHOW;
1212
1213 return;
1214 }
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241 static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
1242 int width)
1243 {
1244 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1245 struct fbcon_ops *ops = info->fbcon_par;
1246
1247 struct fbcon_display *p = &fb_display[vc->vc_num];
1248 u_int y_break;
1249
1250 if (fbcon_is_inactive(vc, info))
1251 return;
1252
1253 if (!height || !width)
1254 return;
1255
1256 if (sy < vc->vc_top && vc->vc_top == logo_lines) {
1257 vc->vc_top = 0;
1258
1259
1260
1261
1262
1263
1264 fbcon_clear_margins(vc, 0);
1265 }
1266
1267
1268
1269 y_break = p->vrows - p->yscroll;
1270 if (sy < y_break && sy + height - 1 >= y_break) {
1271 u_int b = y_break - sy;
1272 ops->clear(vc, info, real_y(p, sy), sx, b, width);
1273 ops->clear(vc, info, real_y(p, sy + b), sx, height - b,
1274 width);
1275 } else
1276 ops->clear(vc, info, real_y(p, sy), sx, height, width);
1277 }
1278
1279 static void fbcon_putcs(struct vc_data *vc, const unsigned short *s,
1280 int count, int ypos, int xpos)
1281 {
1282 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1283 struct fbcon_display *p = &fb_display[vc->vc_num];
1284 struct fbcon_ops *ops = info->fbcon_par;
1285
1286 if (!fbcon_is_inactive(vc, info))
1287 ops->putcs(vc, info, s, count, real_y(p, ypos), xpos,
1288 get_color(vc, info, scr_readw(s), 1),
1289 get_color(vc, info, scr_readw(s), 0));
1290 }
1291
1292 static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos)
1293 {
1294 unsigned short chr;
1295
1296 scr_writew(c, &chr);
1297 fbcon_putcs(vc, &chr, 1, ypos, xpos);
1298 }
1299
1300 static void fbcon_clear_margins(struct vc_data *vc, int bottom_only)
1301 {
1302 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1303 struct fbcon_ops *ops = info->fbcon_par;
1304
1305 if (!fbcon_is_inactive(vc, info))
1306 ops->clear_margins(vc, info, margin_color, bottom_only);
1307 }
1308
1309 static void fbcon_cursor(struct vc_data *vc, int mode)
1310 {
1311 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1312 struct fbcon_ops *ops = info->fbcon_par;
1313 int c = scr_readw((u16 *) vc->vc_pos);
1314
1315 ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
1316
1317 if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1)
1318 return;
1319
1320 if (vc->vc_cursor_type & CUR_SW)
1321 fbcon_del_cursor_work(info);
1322 else
1323 fbcon_add_cursor_work(info);
1324
1325 ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1;
1326
1327 if (!ops->cursor)
1328 return;
1329
1330 ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
1331 get_color(vc, info, c, 0));
1332 }
1333
1334 static int scrollback_phys_max = 0;
1335 static int scrollback_max = 0;
1336 static int scrollback_current = 0;
1337
1338 static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
1339 int unit)
1340 {
1341 struct fbcon_display *p, *t;
1342 struct vc_data **default_mode, *vc;
1343 struct vc_data *svc;
1344 struct fbcon_ops *ops = info->fbcon_par;
1345 int rows, cols;
1346
1347 p = &fb_display[unit];
1348
1349 if (var_to_display(p, var, info))
1350 return;
1351
1352 vc = vc_cons[unit].d;
1353
1354 if (!vc)
1355 return;
1356
1357 default_mode = vc->vc_display_fg;
1358 svc = *default_mode;
1359 t = &fb_display[svc->vc_num];
1360
1361 if (!vc->vc_font.data) {
1362 vc->vc_font.data = (void *)(p->fontdata = t->fontdata);
1363 vc->vc_font.width = (*default_mode)->vc_font.width;
1364 vc->vc_font.height = (*default_mode)->vc_font.height;
1365 vc->vc_font.charcount = (*default_mode)->vc_font.charcount;
1366 p->userfont = t->userfont;
1367 if (p->userfont)
1368 REFCOUNT(p->fontdata)++;
1369 }
1370
1371 var->activate = FB_ACTIVATE_NOW;
1372 info->var.activate = var->activate;
1373 var->yoffset = info->var.yoffset;
1374 var->xoffset = info->var.xoffset;
1375 fb_set_var(info, var);
1376 ops->var = info->var;
1377 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1378 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1379 if (vc->vc_font.charcount == 256) {
1380 vc->vc_hi_font_mask = 0;
1381 } else {
1382 vc->vc_hi_font_mask = 0x100;
1383 if (vc->vc_can_do_color)
1384 vc->vc_complement_mask <<= 1;
1385 }
1386
1387 if (!*svc->uni_pagedict_loc)
1388 con_set_default_unimap(svc);
1389 if (!*vc->uni_pagedict_loc)
1390 con_copy_unimap(vc, svc);
1391
1392 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1393 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1394 cols /= vc->vc_font.width;
1395 rows /= vc->vc_font.height;
1396 vc_resize(vc, cols, rows);
1397
1398 if (con_is_visible(vc)) {
1399 update_screen(vc);
1400 }
1401 }
1402
1403 static __inline__ void ywrap_up(struct vc_data *vc, int count)
1404 {
1405 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1406 struct fbcon_ops *ops = info->fbcon_par;
1407 struct fbcon_display *p = &fb_display[vc->vc_num];
1408
1409 p->yscroll += count;
1410 if (p->yscroll >= p->vrows)
1411 p->yscroll -= p->vrows;
1412 ops->var.xoffset = 0;
1413 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1414 ops->var.vmode |= FB_VMODE_YWRAP;
1415 ops->update_start(info);
1416 scrollback_max += count;
1417 if (scrollback_max > scrollback_phys_max)
1418 scrollback_max = scrollback_phys_max;
1419 scrollback_current = 0;
1420 }
1421
1422 static __inline__ void ywrap_down(struct vc_data *vc, int count)
1423 {
1424 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1425 struct fbcon_ops *ops = info->fbcon_par;
1426 struct fbcon_display *p = &fb_display[vc->vc_num];
1427
1428 p->yscroll -= count;
1429 if (p->yscroll < 0)
1430 p->yscroll += p->vrows;
1431 ops->var.xoffset = 0;
1432 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1433 ops->var.vmode |= FB_VMODE_YWRAP;
1434 ops->update_start(info);
1435 scrollback_max -= count;
1436 if (scrollback_max < 0)
1437 scrollback_max = 0;
1438 scrollback_current = 0;
1439 }
1440
1441 static __inline__ void ypan_up(struct vc_data *vc, int count)
1442 {
1443 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1444 struct fbcon_display *p = &fb_display[vc->vc_num];
1445 struct fbcon_ops *ops = info->fbcon_par;
1446
1447 p->yscroll += count;
1448 if (p->yscroll > p->vrows - vc->vc_rows) {
1449 ops->bmove(vc, info, p->vrows - vc->vc_rows,
1450 0, 0, 0, vc->vc_rows, vc->vc_cols);
1451 p->yscroll -= p->vrows - vc->vc_rows;
1452 }
1453
1454 ops->var.xoffset = 0;
1455 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1456 ops->var.vmode &= ~FB_VMODE_YWRAP;
1457 ops->update_start(info);
1458 fbcon_clear_margins(vc, 1);
1459 scrollback_max += count;
1460 if (scrollback_max > scrollback_phys_max)
1461 scrollback_max = scrollback_phys_max;
1462 scrollback_current = 0;
1463 }
1464
1465 static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
1466 {
1467 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1468 struct fbcon_ops *ops = info->fbcon_par;
1469 struct fbcon_display *p = &fb_display[vc->vc_num];
1470
1471 p->yscroll += count;
1472
1473 if (p->yscroll > p->vrows - vc->vc_rows) {
1474 p->yscroll -= p->vrows - vc->vc_rows;
1475 fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
1476 }
1477
1478 ops->var.xoffset = 0;
1479 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1480 ops->var.vmode &= ~FB_VMODE_YWRAP;
1481 ops->update_start(info);
1482 fbcon_clear_margins(vc, 1);
1483 scrollback_max += count;
1484 if (scrollback_max > scrollback_phys_max)
1485 scrollback_max = scrollback_phys_max;
1486 scrollback_current = 0;
1487 }
1488
1489 static __inline__ void ypan_down(struct vc_data *vc, int count)
1490 {
1491 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1492 struct fbcon_display *p = &fb_display[vc->vc_num];
1493 struct fbcon_ops *ops = info->fbcon_par;
1494
1495 p->yscroll -= count;
1496 if (p->yscroll < 0) {
1497 ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows,
1498 0, vc->vc_rows, vc->vc_cols);
1499 p->yscroll += p->vrows - vc->vc_rows;
1500 }
1501
1502 ops->var.xoffset = 0;
1503 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1504 ops->var.vmode &= ~FB_VMODE_YWRAP;
1505 ops->update_start(info);
1506 fbcon_clear_margins(vc, 1);
1507 scrollback_max -= count;
1508 if (scrollback_max < 0)
1509 scrollback_max = 0;
1510 scrollback_current = 0;
1511 }
1512
1513 static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
1514 {
1515 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1516 struct fbcon_ops *ops = info->fbcon_par;
1517 struct fbcon_display *p = &fb_display[vc->vc_num];
1518
1519 p->yscroll -= count;
1520
1521 if (p->yscroll < 0) {
1522 p->yscroll += p->vrows - vc->vc_rows;
1523 fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
1524 }
1525
1526 ops->var.xoffset = 0;
1527 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1528 ops->var.vmode &= ~FB_VMODE_YWRAP;
1529 ops->update_start(info);
1530 fbcon_clear_margins(vc, 1);
1531 scrollback_max -= count;
1532 if (scrollback_max < 0)
1533 scrollback_max = 0;
1534 scrollback_current = 0;
1535 }
1536
1537 static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
1538 int line, int count, int dy)
1539 {
1540 unsigned short *s = (unsigned short *)
1541 (vc->vc_origin + vc->vc_size_row * line);
1542
1543 while (count--) {
1544 unsigned short *start = s;
1545 unsigned short *le = advance_row(s, 1);
1546 unsigned short c;
1547 int x = 0;
1548 unsigned short attr = 1;
1549
1550 do {
1551 c = scr_readw(s);
1552 if (attr != (c & 0xff00)) {
1553 attr = c & 0xff00;
1554 if (s > start) {
1555 fbcon_putcs(vc, start, s - start,
1556 dy, x);
1557 x += s - start;
1558 start = s;
1559 }
1560 }
1561 console_conditional_schedule();
1562 s++;
1563 } while (s < le);
1564 if (s > start)
1565 fbcon_putcs(vc, start, s - start, dy, x);
1566 console_conditional_schedule();
1567 dy++;
1568 }
1569 }
1570
1571 static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info,
1572 struct fbcon_display *p, int line, int count, int ycount)
1573 {
1574 int offset = ycount * vc->vc_cols;
1575 unsigned short *d = (unsigned short *)
1576 (vc->vc_origin + vc->vc_size_row * line);
1577 unsigned short *s = d + offset;
1578 struct fbcon_ops *ops = info->fbcon_par;
1579
1580 while (count--) {
1581 unsigned short *start = s;
1582 unsigned short *le = advance_row(s, 1);
1583 unsigned short c;
1584 int x = 0;
1585
1586 do {
1587 c = scr_readw(s);
1588
1589 if (c == scr_readw(d)) {
1590 if (s > start) {
1591 ops->bmove(vc, info, line + ycount, x,
1592 line, x, 1, s-start);
1593 x += s - start + 1;
1594 start = s + 1;
1595 } else {
1596 x++;
1597 start++;
1598 }
1599 }
1600
1601 scr_writew(c, d);
1602 console_conditional_schedule();
1603 s++;
1604 d++;
1605 } while (s < le);
1606 if (s > start)
1607 ops->bmove(vc, info, line + ycount, x, line, x, 1,
1608 s-start);
1609 console_conditional_schedule();
1610 if (ycount > 0)
1611 line++;
1612 else {
1613 line--;
1614
1615 s -= vc->vc_size_row;
1616 d -= vc->vc_size_row;
1617 }
1618 }
1619 }
1620
1621 static void fbcon_redraw(struct vc_data *vc, struct fbcon_display *p,
1622 int line, int count, int offset)
1623 {
1624 unsigned short *d = (unsigned short *)
1625 (vc->vc_origin + vc->vc_size_row * line);
1626 unsigned short *s = d + offset;
1627
1628 while (count--) {
1629 unsigned short *start = s;
1630 unsigned short *le = advance_row(s, 1);
1631 unsigned short c;
1632 int x = 0;
1633 unsigned short attr = 1;
1634
1635 do {
1636 c = scr_readw(s);
1637 if (attr != (c & 0xff00)) {
1638 attr = c & 0xff00;
1639 if (s > start) {
1640 fbcon_putcs(vc, start, s - start,
1641 line, x);
1642 x += s - start;
1643 start = s;
1644 }
1645 }
1646 if (c == scr_readw(d)) {
1647 if (s > start) {
1648 fbcon_putcs(vc, start, s - start,
1649 line, x);
1650 x += s - start + 1;
1651 start = s + 1;
1652 } else {
1653 x++;
1654 start++;
1655 }
1656 }
1657 scr_writew(c, d);
1658 console_conditional_schedule();
1659 s++;
1660 d++;
1661 } while (s < le);
1662 if (s > start)
1663 fbcon_putcs(vc, start, s - start, line, x);
1664 console_conditional_schedule();
1665 if (offset > 0)
1666 line++;
1667 else {
1668 line--;
1669
1670 s -= vc->vc_size_row;
1671 d -= vc->vc_size_row;
1672 }
1673 }
1674 }
1675
1676 static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx,
1677 int dy, int dx, int height, int width, u_int y_break)
1678 {
1679 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1680 struct fbcon_ops *ops = info->fbcon_par;
1681 u_int b;
1682
1683 if (sy < y_break && sy + height > y_break) {
1684 b = y_break - sy;
1685 if (dy < sy) {
1686 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1687 y_break);
1688 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1689 height - b, width, y_break);
1690 } else {
1691 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1692 height - b, width, y_break);
1693 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1694 y_break);
1695 }
1696 return;
1697 }
1698
1699 if (dy < y_break && dy + height > y_break) {
1700 b = y_break - dy;
1701 if (dy < sy) {
1702 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1703 y_break);
1704 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1705 height - b, width, y_break);
1706 } else {
1707 fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx,
1708 height - b, width, y_break);
1709 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width,
1710 y_break);
1711 }
1712 return;
1713 }
1714 ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
1715 height, width);
1716 }
1717
1718 static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx,
1719 int height, int width)
1720 {
1721 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1722 struct fbcon_display *p = &fb_display[vc->vc_num];
1723
1724 if (fbcon_is_inactive(vc, info))
1725 return;
1726
1727 if (!width || !height)
1728 return;
1729
1730
1731
1732
1733
1734
1735
1736
1737 fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width,
1738 p->vrows - p->yscroll);
1739 }
1740
1741 static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
1742 enum con_scroll dir, unsigned int count)
1743 {
1744 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
1745 struct fbcon_display *p = &fb_display[vc->vc_num];
1746 int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK;
1747
1748 if (fbcon_is_inactive(vc, info))
1749 return true;
1750
1751 fbcon_cursor(vc, CM_ERASE);
1752
1753
1754
1755
1756
1757
1758
1759 switch (dir) {
1760 case SM_UP:
1761 if (count > vc->vc_rows)
1762 count = vc->vc_rows;
1763 switch (fb_scrollmode(p)) {
1764 case SCROLL_MOVE:
1765 fbcon_redraw_blit(vc, info, p, t, b - t - count,
1766 count);
1767 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1768 scr_memsetw((unsigned short *) (vc->vc_origin +
1769 vc->vc_size_row *
1770 (b - count)),
1771 vc->vc_video_erase_char,
1772 vc->vc_size_row * count);
1773 return true;
1774
1775 case SCROLL_WRAP_MOVE:
1776 if (b - t - count > 3 * vc->vc_rows >> 2) {
1777 if (t > 0)
1778 fbcon_bmove(vc, 0, 0, count, 0, t,
1779 vc->vc_cols);
1780 ywrap_up(vc, count);
1781 if (vc->vc_rows - b > 0)
1782 fbcon_bmove(vc, b - count, 0, b, 0,
1783 vc->vc_rows - b,
1784 vc->vc_cols);
1785 } else if (info->flags & FBINFO_READS_FAST)
1786 fbcon_bmove(vc, t + count, 0, t, 0,
1787 b - t - count, vc->vc_cols);
1788 else
1789 goto redraw_up;
1790 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1791 break;
1792
1793 case SCROLL_PAN_REDRAW:
1794 if ((p->yscroll + count <=
1795 2 * (p->vrows - vc->vc_rows))
1796 && ((!scroll_partial && (b - t == vc->vc_rows))
1797 || (scroll_partial
1798 && (b - t - count >
1799 3 * vc->vc_rows >> 2)))) {
1800 if (t > 0)
1801 fbcon_redraw_move(vc, p, 0, t, count);
1802 ypan_up_redraw(vc, t, count);
1803 if (vc->vc_rows - b > 0)
1804 fbcon_redraw_move(vc, p, b,
1805 vc->vc_rows - b, b);
1806 } else
1807 fbcon_redraw_move(vc, p, t + count, b - t - count, t);
1808 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1809 break;
1810
1811 case SCROLL_PAN_MOVE:
1812 if ((p->yscroll + count <=
1813 2 * (p->vrows - vc->vc_rows))
1814 && ((!scroll_partial && (b - t == vc->vc_rows))
1815 || (scroll_partial
1816 && (b - t - count >
1817 3 * vc->vc_rows >> 2)))) {
1818 if (t > 0)
1819 fbcon_bmove(vc, 0, 0, count, 0, t,
1820 vc->vc_cols);
1821 ypan_up(vc, count);
1822 if (vc->vc_rows - b > 0)
1823 fbcon_bmove(vc, b - count, 0, b, 0,
1824 vc->vc_rows - b,
1825 vc->vc_cols);
1826 } else if (info->flags & FBINFO_READS_FAST)
1827 fbcon_bmove(vc, t + count, 0, t, 0,
1828 b - t - count, vc->vc_cols);
1829 else
1830 goto redraw_up;
1831 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1832 break;
1833
1834 case SCROLL_REDRAW:
1835 redraw_up:
1836 fbcon_redraw(vc, p, t, b - t - count,
1837 count * vc->vc_cols);
1838 fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
1839 scr_memsetw((unsigned short *) (vc->vc_origin +
1840 vc->vc_size_row *
1841 (b - count)),
1842 vc->vc_video_erase_char,
1843 vc->vc_size_row * count);
1844 return true;
1845 }
1846 break;
1847
1848 case SM_DOWN:
1849 if (count > vc->vc_rows)
1850 count = vc->vc_rows;
1851 switch (fb_scrollmode(p)) {
1852 case SCROLL_MOVE:
1853 fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
1854 -count);
1855 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1856 scr_memsetw((unsigned short *) (vc->vc_origin +
1857 vc->vc_size_row *
1858 t),
1859 vc->vc_video_erase_char,
1860 vc->vc_size_row * count);
1861 return true;
1862
1863 case SCROLL_WRAP_MOVE:
1864 if (b - t - count > 3 * vc->vc_rows >> 2) {
1865 if (vc->vc_rows - b > 0)
1866 fbcon_bmove(vc, b, 0, b - count, 0,
1867 vc->vc_rows - b,
1868 vc->vc_cols);
1869 ywrap_down(vc, count);
1870 if (t > 0)
1871 fbcon_bmove(vc, count, 0, 0, 0, t,
1872 vc->vc_cols);
1873 } else if (info->flags & FBINFO_READS_FAST)
1874 fbcon_bmove(vc, t, 0, t + count, 0,
1875 b - t - count, vc->vc_cols);
1876 else
1877 goto redraw_down;
1878 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1879 break;
1880
1881 case SCROLL_PAN_MOVE:
1882 if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1883 && ((!scroll_partial && (b - t == vc->vc_rows))
1884 || (scroll_partial
1885 && (b - t - count >
1886 3 * vc->vc_rows >> 2)))) {
1887 if (vc->vc_rows - b > 0)
1888 fbcon_bmove(vc, b, 0, b - count, 0,
1889 vc->vc_rows - b,
1890 vc->vc_cols);
1891 ypan_down(vc, count);
1892 if (t > 0)
1893 fbcon_bmove(vc, count, 0, 0, 0, t,
1894 vc->vc_cols);
1895 } else if (info->flags & FBINFO_READS_FAST)
1896 fbcon_bmove(vc, t, 0, t + count, 0,
1897 b - t - count, vc->vc_cols);
1898 else
1899 goto redraw_down;
1900 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1901 break;
1902
1903 case SCROLL_PAN_REDRAW:
1904 if ((count - p->yscroll <= p->vrows - vc->vc_rows)
1905 && ((!scroll_partial && (b - t == vc->vc_rows))
1906 || (scroll_partial
1907 && (b - t - count >
1908 3 * vc->vc_rows >> 2)))) {
1909 if (vc->vc_rows - b > 0)
1910 fbcon_redraw_move(vc, p, b, vc->vc_rows - b,
1911 b - count);
1912 ypan_down_redraw(vc, t, count);
1913 if (t > 0)
1914 fbcon_redraw_move(vc, p, count, t, 0);
1915 } else
1916 fbcon_redraw_move(vc, p, t, b - t - count, t + count);
1917 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1918 break;
1919
1920 case SCROLL_REDRAW:
1921 redraw_down:
1922 fbcon_redraw(vc, p, b - 1, b - t - count,
1923 -count * vc->vc_cols);
1924 fbcon_clear(vc, t, 0, count, vc->vc_cols);
1925 scr_memsetw((unsigned short *) (vc->vc_origin +
1926 vc->vc_size_row *
1927 t),
1928 vc->vc_video_erase_char,
1929 vc->vc_size_row * count);
1930 return true;
1931 }
1932 }
1933 return false;
1934 }
1935
1936
1937 static void updatescrollmode_accel(struct fbcon_display *p,
1938 struct fb_info *info,
1939 struct vc_data *vc)
1940 {
1941 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
1942 struct fbcon_ops *ops = info->fbcon_par;
1943 int cap = info->flags;
1944 u16 t = 0;
1945 int ypan = FBCON_SWAP(ops->rotate, info->fix.ypanstep,
1946 info->fix.xpanstep);
1947 int ywrap = FBCON_SWAP(ops->rotate, info->fix.ywrapstep, t);
1948 int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1949 int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
1950 info->var.xres_virtual);
1951 int good_pan = (cap & FBINFO_HWACCEL_YPAN) &&
1952 divides(ypan, vc->vc_font.height) && vyres > yres;
1953 int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) &&
1954 divides(ywrap, vc->vc_font.height) &&
1955 divides(vc->vc_font.height, vyres) &&
1956 divides(vc->vc_font.height, yres);
1957 int reading_fast = cap & FBINFO_READS_FAST;
1958 int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) &&
1959 !(cap & FBINFO_HWACCEL_DISABLED);
1960 int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) &&
1961 !(cap & FBINFO_HWACCEL_DISABLED);
1962
1963 if (good_wrap || good_pan) {
1964 if (reading_fast || fast_copyarea)
1965 p->scrollmode = good_wrap ?
1966 SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
1967 else
1968 p->scrollmode = good_wrap ? SCROLL_REDRAW :
1969 SCROLL_PAN_REDRAW;
1970 } else {
1971 if (reading_fast || (fast_copyarea && !fast_imageblit))
1972 p->scrollmode = SCROLL_MOVE;
1973 else
1974 p->scrollmode = SCROLL_REDRAW;
1975 }
1976 #endif
1977 }
1978
1979 static void updatescrollmode(struct fbcon_display *p,
1980 struct fb_info *info,
1981 struct vc_data *vc)
1982 {
1983 struct fbcon_ops *ops = info->fbcon_par;
1984 int fh = vc->vc_font.height;
1985 int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1986 int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
1987 info->var.xres_virtual);
1988
1989 p->vrows = vyres/fh;
1990 if (yres > (fh * (vc->vc_rows + 1)))
1991 p->vrows -= (yres - (fh * vc->vc_rows)) / fh;
1992 if ((yres % fh) && (vyres % fh < yres % fh))
1993 p->vrows--;
1994
1995
1996 updatescrollmode_accel(p, info, vc);
1997 }
1998
1999 #define PITCH(w) (((w) + 7) >> 3)
2000 #define CALC_FONTSZ(h, p, c) ((h) * (p) * (c))
2001
2002 static int fbcon_resize(struct vc_data *vc, unsigned int width,
2003 unsigned int height, unsigned int user)
2004 {
2005 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2006 struct fbcon_ops *ops = info->fbcon_par;
2007 struct fbcon_display *p = &fb_display[vc->vc_num];
2008 struct fb_var_screeninfo var = info->var;
2009 int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
2010
2011 if (p->userfont && FNTSIZE(vc->vc_font.data)) {
2012 int size;
2013 int pitch = PITCH(vc->vc_font.width);
2014
2015
2016
2017
2018
2019
2020
2021
2022 if (pitch <= 0)
2023 return -EINVAL;
2024 size = CALC_FONTSZ(vc->vc_font.height, pitch, vc->vc_font.charcount);
2025 if (size > FNTSIZE(vc->vc_font.data))
2026 return -EINVAL;
2027 }
2028
2029 virt_w = FBCON_SWAP(ops->rotate, width, height);
2030 virt_h = FBCON_SWAP(ops->rotate, height, width);
2031 virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width,
2032 vc->vc_font.height);
2033 virt_fh = FBCON_SWAP(ops->rotate, vc->vc_font.height,
2034 vc->vc_font.width);
2035 var.xres = virt_w * virt_fw;
2036 var.yres = virt_h * virt_fh;
2037 x_diff = info->var.xres - var.xres;
2038 y_diff = info->var.yres - var.yres;
2039 if (x_diff < 0 || x_diff > virt_fw ||
2040 y_diff < 0 || y_diff > virt_fh) {
2041 const struct fb_videomode *mode;
2042
2043 pr_debug("attempting resize %ix%i\n", var.xres, var.yres);
2044 mode = fb_find_best_mode(&var, &info->modelist);
2045 if (mode == NULL)
2046 return -EINVAL;
2047 display_to_var(&var, p);
2048 fb_videomode_to_var(&var, mode);
2049
2050 if (virt_w > var.xres/virt_fw || virt_h > var.yres/virt_fh)
2051 return -EINVAL;
2052
2053 pr_debug("resize now %ix%i\n", var.xres, var.yres);
2054 if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
2055 var.activate = FB_ACTIVATE_NOW |
2056 FB_ACTIVATE_FORCE;
2057 fb_set_var(info, &var);
2058 }
2059 var_to_display(p, &info->var, info);
2060 ops->var = info->var;
2061 }
2062 updatescrollmode(p, info, vc);
2063 return 0;
2064 }
2065
2066 static int fbcon_switch(struct vc_data *vc)
2067 {
2068 struct fb_info *info, *old_info = NULL;
2069 struct fbcon_ops *ops;
2070 struct fbcon_display *p = &fb_display[vc->vc_num];
2071 struct fb_var_screeninfo var;
2072 int i, ret, prev_console;
2073
2074 info = fbcon_info_from_console(vc->vc_num);
2075 ops = info->fbcon_par;
2076
2077 if (logo_shown >= 0) {
2078 struct vc_data *conp2 = vc_cons[logo_shown].d;
2079
2080 if (conp2->vc_top == logo_lines
2081 && conp2->vc_bottom == conp2->vc_rows)
2082 conp2->vc_top = 0;
2083 logo_shown = FBCON_LOGO_CANSHOW;
2084 }
2085
2086 prev_console = ops->currcon;
2087 if (prev_console != -1)
2088 old_info = fbcon_info_from_console(prev_console);
2089
2090
2091
2092
2093
2094
2095
2096
2097 fbcon_for_each_registered_fb(i) {
2098 if (fbcon_registered_fb[i]->fbcon_par) {
2099 struct fbcon_ops *o = fbcon_registered_fb[i]->fbcon_par;
2100
2101 o->currcon = vc->vc_num;
2102 }
2103 }
2104 memset(&var, 0, sizeof(struct fb_var_screeninfo));
2105 display_to_var(&var, p);
2106 var.activate = FB_ACTIVATE_NOW;
2107
2108
2109
2110
2111
2112 info->var.activate = var.activate;
2113 var.vmode |= info->var.vmode & ~FB_VMODE_MASK;
2114 fb_set_var(info, &var);
2115 ops->var = info->var;
2116
2117 if (old_info != NULL && (old_info != info ||
2118 info->flags & FBINFO_MISC_ALWAYS_SETPAR)) {
2119 if (info->fbops->fb_set_par) {
2120 ret = info->fbops->fb_set_par(info);
2121
2122 if (ret)
2123 printk(KERN_ERR "fbcon_switch: detected "
2124 "unhandled fb_set_par error, "
2125 "error code %d\n", ret);
2126 }
2127
2128 if (old_info != info)
2129 fbcon_del_cursor_work(old_info);
2130 }
2131
2132 if (fbcon_is_inactive(vc, info) ||
2133 ops->blank_state != FB_BLANK_UNBLANK)
2134 fbcon_del_cursor_work(info);
2135 else
2136 fbcon_add_cursor_work(info);
2137
2138 set_blitting_type(vc, info);
2139 ops->cursor_reset = 1;
2140
2141 if (ops->rotate_font && ops->rotate_font(info, vc)) {
2142 ops->rotate = FB_ROTATE_UR;
2143 set_blitting_type(vc, info);
2144 }
2145
2146 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
2147 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
2148
2149 if (vc->vc_font.charcount > 256)
2150 vc->vc_complement_mask <<= 1;
2151
2152 updatescrollmode(p, info, vc);
2153
2154 switch (fb_scrollmode(p)) {
2155 case SCROLL_WRAP_MOVE:
2156 scrollback_phys_max = p->vrows - vc->vc_rows;
2157 break;
2158 case SCROLL_PAN_MOVE:
2159 case SCROLL_PAN_REDRAW:
2160 scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
2161 if (scrollback_phys_max < 0)
2162 scrollback_phys_max = 0;
2163 break;
2164 default:
2165 scrollback_phys_max = 0;
2166 break;
2167 }
2168
2169 scrollback_max = 0;
2170 scrollback_current = 0;
2171
2172 if (!fbcon_is_inactive(vc, info)) {
2173 ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
2174 ops->update_start(info);
2175 }
2176
2177 fbcon_set_palette(vc, color_table);
2178 fbcon_clear_margins(vc, 0);
2179
2180 if (logo_shown == FBCON_LOGO_DRAW) {
2181
2182 logo_shown = fg_console;
2183 fb_show_logo(info, ops->rotate);
2184 update_region(vc,
2185 vc->vc_origin + vc->vc_size_row * vc->vc_top,
2186 vc->vc_size_row * (vc->vc_bottom -
2187 vc->vc_top) / 2);
2188 return 0;
2189 }
2190 return 1;
2191 }
2192
2193 static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info,
2194 int blank)
2195 {
2196 if (blank) {
2197 unsigned short charmask = vc->vc_hi_font_mask ?
2198 0x1ff : 0xff;
2199 unsigned short oldc;
2200
2201 oldc = vc->vc_video_erase_char;
2202 vc->vc_video_erase_char &= charmask;
2203 fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols);
2204 vc->vc_video_erase_char = oldc;
2205 }
2206 }
2207
2208 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
2209 {
2210 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2211 struct fbcon_ops *ops = info->fbcon_par;
2212
2213 if (mode_switch) {
2214 struct fb_var_screeninfo var = info->var;
2215
2216 ops->graphics = 1;
2217
2218 if (!blank) {
2219 var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE |
2220 FB_ACTIVATE_KD_TEXT;
2221 fb_set_var(info, &var);
2222 ops->graphics = 0;
2223 ops->var = info->var;
2224 }
2225 }
2226
2227 if (!fbcon_is_inactive(vc, info)) {
2228 if (ops->blank_state != blank) {
2229 ops->blank_state = blank;
2230 fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
2231 ops->cursor_flash = (!blank);
2232
2233 if (fb_blank(info, blank))
2234 fbcon_generic_blank(vc, info, blank);
2235 }
2236
2237 if (!blank)
2238 update_screen(vc);
2239 }
2240
2241 if (mode_switch || fbcon_is_inactive(vc, info) ||
2242 ops->blank_state != FB_BLANK_UNBLANK)
2243 fbcon_del_cursor_work(info);
2244 else
2245 fbcon_add_cursor_work(info);
2246
2247 return 0;
2248 }
2249
2250 static int fbcon_debug_enter(struct vc_data *vc)
2251 {
2252 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2253 struct fbcon_ops *ops = info->fbcon_par;
2254
2255 ops->save_graphics = ops->graphics;
2256 ops->graphics = 0;
2257 if (info->fbops->fb_debug_enter)
2258 info->fbops->fb_debug_enter(info);
2259 fbcon_set_palette(vc, color_table);
2260 return 0;
2261 }
2262
2263 static int fbcon_debug_leave(struct vc_data *vc)
2264 {
2265 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2266 struct fbcon_ops *ops = info->fbcon_par;
2267
2268 ops->graphics = ops->save_graphics;
2269 if (info->fbops->fb_debug_leave)
2270 info->fbops->fb_debug_leave(info);
2271 return 0;
2272 }
2273
2274 static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
2275 {
2276 u8 *fontdata = vc->vc_font.data;
2277 u8 *data = font->data;
2278 int i, j;
2279
2280 font->width = vc->vc_font.width;
2281 font->height = vc->vc_font.height;
2282 font->charcount = vc->vc_hi_font_mask ? 512 : 256;
2283 if (!font->data)
2284 return 0;
2285
2286 if (font->width <= 8) {
2287 j = vc->vc_font.height;
2288 if (font->charcount * j > FNTSIZE(fontdata))
2289 return -EINVAL;
2290
2291 for (i = 0; i < font->charcount; i++) {
2292 memcpy(data, fontdata, j);
2293 memset(data + j, 0, 32 - j);
2294 data += 32;
2295 fontdata += j;
2296 }
2297 } else if (font->width <= 16) {
2298 j = vc->vc_font.height * 2;
2299 if (font->charcount * j > FNTSIZE(fontdata))
2300 return -EINVAL;
2301
2302 for (i = 0; i < font->charcount; i++) {
2303 memcpy(data, fontdata, j);
2304 memset(data + j, 0, 64 - j);
2305 data += 64;
2306 fontdata += j;
2307 }
2308 } else if (font->width <= 24) {
2309 if (font->charcount * (vc->vc_font.height * sizeof(u32)) > FNTSIZE(fontdata))
2310 return -EINVAL;
2311
2312 for (i = 0; i < font->charcount; i++) {
2313 for (j = 0; j < vc->vc_font.height; j++) {
2314 *data++ = fontdata[0];
2315 *data++ = fontdata[1];
2316 *data++ = fontdata[2];
2317 fontdata += sizeof(u32);
2318 }
2319 memset(data, 0, 3 * (32 - j));
2320 data += 3 * (32 - j);
2321 }
2322 } else {
2323 j = vc->vc_font.height * 4;
2324 if (font->charcount * j > FNTSIZE(fontdata))
2325 return -EINVAL;
2326
2327 for (i = 0; i < font->charcount; i++) {
2328 memcpy(data, fontdata, j);
2329 memset(data + j, 0, 128 - j);
2330 data += 128;
2331 fontdata += j;
2332 }
2333 }
2334 return 0;
2335 }
2336
2337
2338 static void set_vc_hi_font(struct vc_data *vc, bool set)
2339 {
2340 if (!set) {
2341 vc->vc_hi_font_mask = 0;
2342 if (vc->vc_can_do_color) {
2343 vc->vc_complement_mask >>= 1;
2344 vc->vc_s_complement_mask >>= 1;
2345 }
2346
2347
2348 if (vc->vc_can_do_color) {
2349 unsigned short *cp =
2350 (unsigned short *) vc->vc_origin;
2351 int count = vc->vc_screenbuf_size / 2;
2352 unsigned short c;
2353 for (; count > 0; count--, cp++) {
2354 c = scr_readw(cp);
2355 scr_writew(((c & 0xfe00) >> 1) |
2356 (c & 0xff), cp);
2357 }
2358 c = vc->vc_video_erase_char;
2359 vc->vc_video_erase_char =
2360 ((c & 0xfe00) >> 1) | (c & 0xff);
2361 vc->vc_attr >>= 1;
2362 }
2363 } else {
2364 vc->vc_hi_font_mask = 0x100;
2365 if (vc->vc_can_do_color) {
2366 vc->vc_complement_mask <<= 1;
2367 vc->vc_s_complement_mask <<= 1;
2368 }
2369
2370
2371 {
2372 unsigned short *cp =
2373 (unsigned short *) vc->vc_origin;
2374 int count = vc->vc_screenbuf_size / 2;
2375 unsigned short c;
2376 for (; count > 0; count--, cp++) {
2377 unsigned short newc;
2378 c = scr_readw(cp);
2379 if (vc->vc_can_do_color)
2380 newc =
2381 ((c & 0xff00) << 1) | (c &
2382 0xff);
2383 else
2384 newc = c & ~0x100;
2385 scr_writew(newc, cp);
2386 }
2387 c = vc->vc_video_erase_char;
2388 if (vc->vc_can_do_color) {
2389 vc->vc_video_erase_char =
2390 ((c & 0xff00) << 1) | (c & 0xff);
2391 vc->vc_attr <<= 1;
2392 } else
2393 vc->vc_video_erase_char = c & ~0x100;
2394 }
2395 }
2396 }
2397
2398 static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount,
2399 const u8 * data, int userfont)
2400 {
2401 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2402 struct fbcon_ops *ops = info->fbcon_par;
2403 struct fbcon_display *p = &fb_display[vc->vc_num];
2404 int resize, ret, old_userfont, old_width, old_height, old_charcount;
2405 char *old_data = NULL;
2406
2407 resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
2408 if (p->userfont)
2409 old_data = vc->vc_font.data;
2410 vc->vc_font.data = (void *)(p->fontdata = data);
2411 old_userfont = p->userfont;
2412 if ((p->userfont = userfont))
2413 REFCOUNT(data)++;
2414
2415 old_width = vc->vc_font.width;
2416 old_height = vc->vc_font.height;
2417 old_charcount = vc->vc_font.charcount;
2418
2419 vc->vc_font.width = w;
2420 vc->vc_font.height = h;
2421 vc->vc_font.charcount = charcount;
2422 if (vc->vc_hi_font_mask && charcount == 256)
2423 set_vc_hi_font(vc, false);
2424 else if (!vc->vc_hi_font_mask && charcount == 512)
2425 set_vc_hi_font(vc, true);
2426
2427 if (resize) {
2428 int cols, rows;
2429
2430 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2431 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2432 cols /= w;
2433 rows /= h;
2434 ret = vc_resize(vc, cols, rows);
2435 if (ret)
2436 goto err_out;
2437 } else if (con_is_visible(vc)
2438 && vc->vc_mode == KD_TEXT) {
2439 fbcon_clear_margins(vc, 0);
2440 update_screen(vc);
2441 }
2442
2443 if (old_data && (--REFCOUNT(old_data) == 0))
2444 kfree(old_data - FONT_EXTRA_WORDS * sizeof(int));
2445 return 0;
2446
2447 err_out:
2448 p->fontdata = old_data;
2449 vc->vc_font.data = (void *)old_data;
2450
2451 if (userfont) {
2452 p->userfont = old_userfont;
2453 REFCOUNT(data)--;
2454 }
2455
2456 vc->vc_font.width = old_width;
2457 vc->vc_font.height = old_height;
2458 vc->vc_font.charcount = old_charcount;
2459
2460 return ret;
2461 }
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475 static int fbcon_set_font(struct vc_data *vc, struct console_font *font,
2476 unsigned int flags)
2477 {
2478 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2479 unsigned charcount = font->charcount;
2480 int w = font->width;
2481 int h = font->height;
2482 int size;
2483 int i, csum;
2484 u8 *new_data, *data = font->data;
2485 int pitch = PITCH(font->width);
2486
2487
2488
2489 if (charcount != 256 && charcount != 512)
2490 return -EINVAL;
2491
2492
2493 if (w > FBCON_SWAP(info->var.rotate, info->var.xres, info->var.yres) ||
2494 h > FBCON_SWAP(info->var.rotate, info->var.yres, info->var.xres))
2495 return -EINVAL;
2496
2497
2498 if (!(info->pixmap.blit_x & (1 << (font->width - 1))) ||
2499 !(info->pixmap.blit_y & (1 << (font->height - 1))))
2500 return -EINVAL;
2501
2502
2503 if (fbcon_invalid_charcount(info, charcount))
2504 return -EINVAL;
2505
2506 size = CALC_FONTSZ(h, pitch, charcount);
2507
2508 new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER);
2509
2510 if (!new_data)
2511 return -ENOMEM;
2512
2513 memset(new_data, 0, FONT_EXTRA_WORDS * sizeof(int));
2514
2515 new_data += FONT_EXTRA_WORDS * sizeof(int);
2516 FNTSIZE(new_data) = size;
2517 REFCOUNT(new_data) = 0;
2518 for (i=0; i< charcount; i++) {
2519 memcpy(new_data + i*h*pitch, data + i*32*pitch, h*pitch);
2520 }
2521
2522
2523
2524 csum = crc32(0, new_data, size);
2525
2526 FNTSUM(new_data) = csum;
2527
2528 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2529 struct vc_data *tmp = vc_cons[i].d;
2530
2531 if (fb_display[i].userfont &&
2532 fb_display[i].fontdata &&
2533 FNTSUM(fb_display[i].fontdata) == csum &&
2534 FNTSIZE(fb_display[i].fontdata) == size &&
2535 tmp->vc_font.width == w &&
2536 !memcmp(fb_display[i].fontdata, new_data, size)) {
2537 kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
2538 new_data = (u8 *)fb_display[i].fontdata;
2539 break;
2540 }
2541 }
2542 return fbcon_do_set_font(vc, font->width, font->height, charcount, new_data, 1);
2543 }
2544
2545 static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name)
2546 {
2547 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2548 const struct font_desc *f;
2549
2550 if (!name)
2551 f = get_default_font(info->var.xres, info->var.yres,
2552 info->pixmap.blit_x, info->pixmap.blit_y);
2553 else if (!(f = find_font(name)))
2554 return -ENOENT;
2555
2556 font->width = f->width;
2557 font->height = f->height;
2558 return fbcon_do_set_font(vc, f->width, f->height, f->charcount, f->data, 0);
2559 }
2560
2561 static u16 palette_red[16];
2562 static u16 palette_green[16];
2563 static u16 palette_blue[16];
2564
2565 static struct fb_cmap palette_cmap = {
2566 0, 16, palette_red, palette_green, palette_blue, NULL
2567 };
2568
2569 static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table)
2570 {
2571 struct fb_info *info = fbcon_info_from_console(vc->vc_num);
2572 int i, j, k, depth;
2573 u8 val;
2574
2575 if (fbcon_is_inactive(vc, info))
2576 return;
2577
2578 if (!con_is_visible(vc))
2579 return;
2580
2581 depth = fb_get_color_depth(&info->var, &info->fix);
2582 if (depth > 3) {
2583 for (i = j = 0; i < 16; i++) {
2584 k = table[i];
2585 val = vc->vc_palette[j++];
2586 palette_red[k] = (val << 8) | val;
2587 val = vc->vc_palette[j++];
2588 palette_green[k] = (val << 8) | val;
2589 val = vc->vc_palette[j++];
2590 palette_blue[k] = (val << 8) | val;
2591 }
2592 palette_cmap.len = 16;
2593 palette_cmap.start = 0;
2594
2595
2596
2597
2598 } else
2599 fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap);
2600
2601 fb_set_cmap(&palette_cmap, info);
2602 }
2603
2604 static u16 *fbcon_screen_pos(const struct vc_data *vc, int offset)
2605 {
2606 return (u16 *) (vc->vc_origin + offset);
2607 }
2608
2609 static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
2610 int *px, int *py)
2611 {
2612 unsigned long ret;
2613 int x, y;
2614
2615 if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
2616 unsigned long offset = (pos - vc->vc_origin) / 2;
2617
2618 x = offset % vc->vc_cols;
2619 y = offset / vc->vc_cols;
2620 ret = pos + (vc->vc_cols - x) * 2;
2621 } else {
2622
2623 x = y = 0;
2624 ret = vc->vc_origin;
2625 }
2626 if (px)
2627 *px = x;
2628 if (py)
2629 *py = y;
2630 return ret;
2631 }
2632
2633
2634
2635 static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
2636 {
2637 while (cnt--) {
2638 u16 a = scr_readw(p);
2639 if (!vc->vc_can_do_color)
2640 a ^= 0x0800;
2641 else if (vc->vc_hi_font_mask == 0x100)
2642 a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) |
2643 (((a) & 0x0e00) << 4);
2644 else
2645 a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
2646 (((a) & 0x0700) << 4);
2647 scr_writew(a, p++);
2648 }
2649 }
2650
2651 void fbcon_suspended(struct fb_info *info)
2652 {
2653 struct vc_data *vc = NULL;
2654 struct fbcon_ops *ops = info->fbcon_par;
2655
2656 if (!ops || ops->currcon < 0)
2657 return;
2658 vc = vc_cons[ops->currcon].d;
2659
2660
2661 fbcon_cursor(vc, CM_ERASE);
2662 }
2663
2664 void fbcon_resumed(struct fb_info *info)
2665 {
2666 struct vc_data *vc;
2667 struct fbcon_ops *ops = info->fbcon_par;
2668
2669 if (!ops || ops->currcon < 0)
2670 return;
2671 vc = vc_cons[ops->currcon].d;
2672
2673 update_screen(vc);
2674 }
2675
2676 static void fbcon_modechanged(struct fb_info *info)
2677 {
2678 struct fbcon_ops *ops = info->fbcon_par;
2679 struct vc_data *vc;
2680 struct fbcon_display *p;
2681 int rows, cols;
2682
2683 if (!ops || ops->currcon < 0)
2684 return;
2685 vc = vc_cons[ops->currcon].d;
2686 if (vc->vc_mode != KD_TEXT ||
2687 fbcon_info_from_console(ops->currcon) != info)
2688 return;
2689
2690 p = &fb_display[vc->vc_num];
2691 set_blitting_type(vc, info);
2692
2693 if (con_is_visible(vc)) {
2694 var_to_display(p, &info->var, info);
2695 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2696 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2697 cols /= vc->vc_font.width;
2698 rows /= vc->vc_font.height;
2699 vc_resize(vc, cols, rows);
2700 updatescrollmode(p, info, vc);
2701 scrollback_max = 0;
2702 scrollback_current = 0;
2703
2704 if (!fbcon_is_inactive(vc, info)) {
2705 ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
2706 ops->update_start(info);
2707 }
2708
2709 fbcon_set_palette(vc, color_table);
2710 update_screen(vc);
2711 }
2712 }
2713
2714 static void fbcon_set_all_vcs(struct fb_info *info)
2715 {
2716 struct fbcon_ops *ops = info->fbcon_par;
2717 struct vc_data *vc;
2718 struct fbcon_display *p;
2719 int i, rows, cols, fg = -1;
2720
2721 if (!ops || ops->currcon < 0)
2722 return;
2723
2724 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2725 vc = vc_cons[i].d;
2726 if (!vc || vc->vc_mode != KD_TEXT ||
2727 fbcon_info_from_console(i) != info)
2728 continue;
2729
2730 if (con_is_visible(vc)) {
2731 fg = i;
2732 continue;
2733 }
2734
2735 p = &fb_display[vc->vc_num];
2736 set_blitting_type(vc, info);
2737 var_to_display(p, &info->var, info);
2738 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2739 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2740 cols /= vc->vc_font.width;
2741 rows /= vc->vc_font.height;
2742 vc_resize(vc, cols, rows);
2743 }
2744
2745 if (fg != -1)
2746 fbcon_modechanged(info);
2747 }
2748
2749
2750 void fbcon_update_vcs(struct fb_info *info, bool all)
2751 {
2752 if (all)
2753 fbcon_set_all_vcs(info);
2754 else
2755 fbcon_modechanged(info);
2756 }
2757 EXPORT_SYMBOL(fbcon_update_vcs);
2758
2759
2760 int fbcon_modechange_possible(struct fb_info *info, struct fb_var_screeninfo *var)
2761 {
2762 struct fbcon_ops *ops = info->fbcon_par;
2763 struct vc_data *vc;
2764 unsigned int i;
2765
2766 WARN_CONSOLE_UNLOCKED();
2767
2768 if (!ops)
2769 return 0;
2770
2771
2772 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2773 vc = vc_cons[i].d;
2774 if (!vc || vc->vc_mode != KD_TEXT ||
2775 fbcon_info_from_console(i) != info)
2776 continue;
2777
2778 if (vc->vc_font.width > FBCON_SWAP(var->rotate, var->xres, var->yres) ||
2779 vc->vc_font.height > FBCON_SWAP(var->rotate, var->yres, var->xres))
2780 return -EINVAL;
2781 }
2782
2783 return 0;
2784 }
2785 EXPORT_SYMBOL_GPL(fbcon_modechange_possible);
2786
2787 int fbcon_mode_deleted(struct fb_info *info,
2788 struct fb_videomode *mode)
2789 {
2790 struct fb_info *fb_info;
2791 struct fbcon_display *p;
2792 int i, j, found = 0;
2793
2794
2795 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2796 j = con2fb_map[i];
2797 if (j == -1)
2798 continue;
2799 fb_info = fbcon_registered_fb[j];
2800 if (fb_info != info)
2801 continue;
2802 p = &fb_display[i];
2803 if (!p || !p->mode)
2804 continue;
2805 if (fb_mode_is_equal(p->mode, mode)) {
2806 found = 1;
2807 break;
2808 }
2809 }
2810 return found;
2811 }
2812
2813 #ifdef CONFIG_VT_HW_CONSOLE_BINDING
2814 static void fbcon_unbind(void)
2815 {
2816 int ret;
2817
2818 ret = do_unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc,
2819 fbcon_is_default);
2820
2821 if (!ret)
2822 fbcon_has_console_bind = 0;
2823 }
2824 #else
2825 static inline void fbcon_unbind(void) {}
2826 #endif
2827
2828 void fbcon_fb_unbind(struct fb_info *info)
2829 {
2830 int i, new_idx = -1;
2831 int idx = info->node;
2832
2833 console_lock();
2834
2835 if (!fbcon_has_console_bind) {
2836 console_unlock();
2837 return;
2838 }
2839
2840 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2841 if (con2fb_map[i] != idx &&
2842 con2fb_map[i] != -1) {
2843 new_idx = con2fb_map[i];
2844 break;
2845 }
2846 }
2847
2848 if (new_idx != -1) {
2849 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2850 if (con2fb_map[i] == idx)
2851 set_con2fb_map(i, new_idx, 0);
2852 }
2853 } else {
2854 struct fb_info *info = fbcon_registered_fb[idx];
2855
2856
2857
2858
2859
2860
2861
2862 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2863 if (con2fb_map[i] == idx) {
2864 con2fb_map[i] = -1;
2865 if (!search_fb_in_map(idx)) {
2866 con2fb_release_oldinfo(vc_cons[i].d,
2867 info, NULL);
2868 }
2869 }
2870 }
2871 fbcon_unbind();
2872 }
2873
2874 console_unlock();
2875 }
2876
2877 void fbcon_fb_unregistered(struct fb_info *info)
2878 {
2879 int i, idx;
2880
2881 console_lock();
2882
2883 fbcon_registered_fb[info->node] = NULL;
2884 fbcon_num_registered_fb--;
2885
2886 if (deferred_takeover) {
2887 console_unlock();
2888 return;
2889 }
2890
2891 idx = info->node;
2892 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2893 if (con2fb_map[i] == idx)
2894 con2fb_map[i] = -1;
2895 }
2896
2897 if (idx == info_idx) {
2898 info_idx = -1;
2899
2900 fbcon_for_each_registered_fb(i) {
2901 info_idx = i;
2902 break;
2903 }
2904 }
2905
2906 if (info_idx != -1) {
2907 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2908 if (con2fb_map[i] == -1)
2909 con2fb_map[i] = info_idx;
2910 }
2911 }
2912
2913 if (primary_device == idx)
2914 primary_device = -1;
2915
2916 if (!fbcon_num_registered_fb)
2917 do_unregister_con_driver(&fb_con);
2918 console_unlock();
2919 }
2920
2921 void fbcon_remap_all(struct fb_info *info)
2922 {
2923 int i, idx = info->node;
2924
2925 console_lock();
2926 if (deferred_takeover) {
2927 for (i = first_fb_vc; i <= last_fb_vc; i++)
2928 con2fb_map_boot[i] = idx;
2929 fbcon_map_override();
2930 console_unlock();
2931 return;
2932 }
2933
2934 for (i = first_fb_vc; i <= last_fb_vc; i++)
2935 set_con2fb_map(i, idx, 0);
2936
2937 if (con_is_bound(&fb_con)) {
2938 printk(KERN_INFO "fbcon: Remapping primary device, "
2939 "fb%i, to tty %i-%i\n", idx,
2940 first_fb_vc + 1, last_fb_vc + 1);
2941 info_idx = idx;
2942 }
2943 console_unlock();
2944 }
2945
2946 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
2947 static void fbcon_select_primary(struct fb_info *info)
2948 {
2949 if (!map_override && primary_device == -1 &&
2950 fb_is_primary_device(info)) {
2951 int i;
2952
2953 printk(KERN_INFO "fbcon: %s (fb%i) is primary device\n",
2954 info->fix.id, info->node);
2955 primary_device = info->node;
2956
2957 for (i = first_fb_vc; i <= last_fb_vc; i++)
2958 con2fb_map_boot[i] = primary_device;
2959
2960 if (con_is_bound(&fb_con)) {
2961 printk(KERN_INFO "fbcon: Remapping primary device, "
2962 "fb%i, to tty %i-%i\n", info->node,
2963 first_fb_vc + 1, last_fb_vc + 1);
2964 info_idx = primary_device;
2965 }
2966 }
2967
2968 }
2969 #else
2970 static inline void fbcon_select_primary(struct fb_info *info)
2971 {
2972 return;
2973 }
2974 #endif
2975
2976 static bool lockless_register_fb;
2977 module_param_named_unsafe(lockless_register_fb, lockless_register_fb, bool, 0400);
2978 MODULE_PARM_DESC(lockless_register_fb,
2979 "Lockless framebuffer registration for debugging [default=off]");
2980
2981
2982 static int do_fb_registered(struct fb_info *info)
2983 {
2984 int ret = 0, i, idx;
2985
2986 WARN_CONSOLE_UNLOCKED();
2987
2988 fbcon_registered_fb[info->node] = info;
2989 fbcon_num_registered_fb++;
2990
2991 idx = info->node;
2992 fbcon_select_primary(info);
2993
2994 if (deferred_takeover) {
2995 pr_info("fbcon: Deferring console take-over\n");
2996 return 0;
2997 }
2998
2999 if (info_idx == -1) {
3000 for (i = first_fb_vc; i <= last_fb_vc; i++) {
3001 if (con2fb_map_boot[i] == idx) {
3002 info_idx = idx;
3003 break;
3004 }
3005 }
3006
3007 if (info_idx != -1)
3008 ret = do_fbcon_takeover(1);
3009 } else {
3010 for (i = first_fb_vc; i <= last_fb_vc; i++) {
3011 if (con2fb_map_boot[i] == idx)
3012 set_con2fb_map(i, idx, 0);
3013 }
3014 }
3015
3016 return ret;
3017 }
3018
3019 int fbcon_fb_registered(struct fb_info *info)
3020 {
3021 int ret;
3022
3023 if (!lockless_register_fb)
3024 console_lock();
3025 else
3026 atomic_inc(&ignore_console_lock_warning);
3027
3028 ret = do_fb_registered(info);
3029
3030 if (!lockless_register_fb)
3031 console_unlock();
3032 else
3033 atomic_dec(&ignore_console_lock_warning);
3034
3035 return ret;
3036 }
3037
3038 void fbcon_fb_blanked(struct fb_info *info, int blank)
3039 {
3040 struct fbcon_ops *ops = info->fbcon_par;
3041 struct vc_data *vc;
3042
3043 if (!ops || ops->currcon < 0)
3044 return;
3045
3046 vc = vc_cons[ops->currcon].d;
3047 if (vc->vc_mode != KD_TEXT ||
3048 fbcon_info_from_console(ops->currcon) != info)
3049 return;
3050
3051 if (con_is_visible(vc)) {
3052 if (blank)
3053 do_blank_screen(0);
3054 else
3055 do_unblank_screen(0);
3056 }
3057 ops->blank_state = blank;
3058 }
3059
3060 void fbcon_new_modelist(struct fb_info *info)
3061 {
3062 int i;
3063 struct vc_data *vc;
3064 struct fb_var_screeninfo var;
3065 const struct fb_videomode *mode;
3066
3067 for (i = first_fb_vc; i <= last_fb_vc; i++) {
3068 if (fbcon_info_from_console(i) != info)
3069 continue;
3070 if (!fb_display[i].mode)
3071 continue;
3072 vc = vc_cons[i].d;
3073 display_to_var(&var, &fb_display[i]);
3074 mode = fb_find_nearest_mode(fb_display[i].mode,
3075 &info->modelist);
3076 fb_videomode_to_var(&var, mode);
3077 fbcon_set_disp(info, &var, vc->vc_num);
3078 }
3079 }
3080
3081 void fbcon_get_requirement(struct fb_info *info,
3082 struct fb_blit_caps *caps)
3083 {
3084 struct vc_data *vc;
3085
3086 if (caps->flags) {
3087 int i, charcnt;
3088
3089 for (i = first_fb_vc; i <= last_fb_vc; i++) {
3090 vc = vc_cons[i].d;
3091 if (vc && vc->vc_mode == KD_TEXT &&
3092 info->node == con2fb_map[i]) {
3093 caps->x |= 1 << (vc->vc_font.width - 1);
3094 caps->y |= 1 << (vc->vc_font.height - 1);
3095 charcnt = vc->vc_font.charcount;
3096 if (caps->len < charcnt)
3097 caps->len = charcnt;
3098 }
3099 }
3100 } else {
3101 vc = vc_cons[fg_console].d;
3102
3103 if (vc && vc->vc_mode == KD_TEXT &&
3104 info->node == con2fb_map[fg_console]) {
3105 caps->x = 1 << (vc->vc_font.width - 1);
3106 caps->y = 1 << (vc->vc_font.height - 1);
3107 caps->len = vc->vc_font.charcount;
3108 }
3109 }
3110 }
3111
3112 int fbcon_set_con2fb_map_ioctl(void __user *argp)
3113 {
3114 struct fb_con2fbmap con2fb;
3115 int ret;
3116
3117 if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3118 return -EFAULT;
3119 if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3120 return -EINVAL;
3121 if (con2fb.framebuffer >= FB_MAX)
3122 return -EINVAL;
3123 if (!fbcon_registered_fb[con2fb.framebuffer])
3124 request_module("fb%d", con2fb.framebuffer);
3125 if (!fbcon_registered_fb[con2fb.framebuffer]) {
3126 return -EINVAL;
3127 }
3128
3129 console_lock();
3130 ret = set_con2fb_map(con2fb.console - 1,
3131 con2fb.framebuffer, 1);
3132 console_unlock();
3133
3134 return ret;
3135 }
3136
3137 int fbcon_get_con2fb_map_ioctl(void __user *argp)
3138 {
3139 struct fb_con2fbmap con2fb;
3140
3141 if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
3142 return -EFAULT;
3143 if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
3144 return -EINVAL;
3145
3146 console_lock();
3147 con2fb.framebuffer = con2fb_map[con2fb.console - 1];
3148 console_unlock();
3149
3150 return copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;
3151 }
3152
3153
3154
3155
3156
3157 static const struct consw fb_con = {
3158 .owner = THIS_MODULE,
3159 .con_startup = fbcon_startup,
3160 .con_init = fbcon_init,
3161 .con_deinit = fbcon_deinit,
3162 .con_clear = fbcon_clear,
3163 .con_putc = fbcon_putc,
3164 .con_putcs = fbcon_putcs,
3165 .con_cursor = fbcon_cursor,
3166 .con_scroll = fbcon_scroll,
3167 .con_switch = fbcon_switch,
3168 .con_blank = fbcon_blank,
3169 .con_font_set = fbcon_set_font,
3170 .con_font_get = fbcon_get_font,
3171 .con_font_default = fbcon_set_def_font,
3172 .con_set_palette = fbcon_set_palette,
3173 .con_invert_region = fbcon_invert_region,
3174 .con_screen_pos = fbcon_screen_pos,
3175 .con_getxy = fbcon_getxy,
3176 .con_resize = fbcon_resize,
3177 .con_debug_enter = fbcon_debug_enter,
3178 .con_debug_leave = fbcon_debug_leave,
3179 };
3180
3181 static ssize_t store_rotate(struct device *device,
3182 struct device_attribute *attr, const char *buf,
3183 size_t count)
3184 {
3185 struct fb_info *info;
3186 int rotate, idx;
3187 char **last = NULL;
3188
3189 console_lock();
3190 idx = con2fb_map[fg_console];
3191
3192 if (idx == -1 || fbcon_registered_fb[idx] == NULL)
3193 goto err;
3194
3195 info = fbcon_registered_fb[idx];
3196 rotate = simple_strtoul(buf, last, 0);
3197 fbcon_rotate(info, rotate);
3198 err:
3199 console_unlock();
3200 return count;
3201 }
3202
3203 static ssize_t store_rotate_all(struct device *device,
3204 struct device_attribute *attr,const char *buf,
3205 size_t count)
3206 {
3207 struct fb_info *info;
3208 int rotate, idx;
3209 char **last = NULL;
3210
3211 console_lock();
3212 idx = con2fb_map[fg_console];
3213
3214 if (idx == -1 || fbcon_registered_fb[idx] == NULL)
3215 goto err;
3216
3217 info = fbcon_registered_fb[idx];
3218 rotate = simple_strtoul(buf, last, 0);
3219 fbcon_rotate_all(info, rotate);
3220 err:
3221 console_unlock();
3222 return count;
3223 }
3224
3225 static ssize_t show_rotate(struct device *device,
3226 struct device_attribute *attr,char *buf)
3227 {
3228 struct fb_info *info;
3229 int rotate = 0, idx;
3230
3231 console_lock();
3232 idx = con2fb_map[fg_console];
3233
3234 if (idx == -1 || fbcon_registered_fb[idx] == NULL)
3235 goto err;
3236
3237 info = fbcon_registered_fb[idx];
3238 rotate = fbcon_get_rotate(info);
3239 err:
3240 console_unlock();
3241 return sysfs_emit(buf, "%d\n", rotate);
3242 }
3243
3244 static ssize_t show_cursor_blink(struct device *device,
3245 struct device_attribute *attr, char *buf)
3246 {
3247 struct fb_info *info;
3248 struct fbcon_ops *ops;
3249 int idx, blink = -1;
3250
3251 console_lock();
3252 idx = con2fb_map[fg_console];
3253
3254 if (idx == -1 || fbcon_registered_fb[idx] == NULL)
3255 goto err;
3256
3257 info = fbcon_registered_fb[idx];
3258 ops = info->fbcon_par;
3259
3260 if (!ops)
3261 goto err;
3262
3263 blink = delayed_work_pending(&ops->cursor_work);
3264 err:
3265 console_unlock();
3266 return sysfs_emit(buf, "%d\n", blink);
3267 }
3268
3269 static ssize_t store_cursor_blink(struct device *device,
3270 struct device_attribute *attr,
3271 const char *buf, size_t count)
3272 {
3273 struct fb_info *info;
3274 int blink, idx;
3275 char **last = NULL;
3276
3277 console_lock();
3278 idx = con2fb_map[fg_console];
3279
3280 if (idx == -1 || fbcon_registered_fb[idx] == NULL)
3281 goto err;
3282
3283 info = fbcon_registered_fb[idx];
3284
3285 if (!info->fbcon_par)
3286 goto err;
3287
3288 blink = simple_strtoul(buf, last, 0);
3289
3290 if (blink) {
3291 fbcon_cursor_noblink = 0;
3292 fbcon_add_cursor_work(info);
3293 } else {
3294 fbcon_cursor_noblink = 1;
3295 fbcon_del_cursor_work(info);
3296 }
3297
3298 err:
3299 console_unlock();
3300 return count;
3301 }
3302
3303 static struct device_attribute device_attrs[] = {
3304 __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
3305 __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all),
3306 __ATTR(cursor_blink, S_IRUGO|S_IWUSR, show_cursor_blink,
3307 store_cursor_blink),
3308 };
3309
3310 static int fbcon_init_device(void)
3311 {
3312 int i, error = 0;
3313
3314 fbcon_has_sysfs = 1;
3315
3316 for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
3317 error = device_create_file(fbcon_device, &device_attrs[i]);
3318
3319 if (error)
3320 break;
3321 }
3322
3323 if (error) {
3324 while (--i >= 0)
3325 device_remove_file(fbcon_device, &device_attrs[i]);
3326
3327 fbcon_has_sysfs = 0;
3328 }
3329
3330 return 0;
3331 }
3332
3333 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3334 static void fbcon_register_existing_fbs(struct work_struct *work)
3335 {
3336 int i;
3337
3338 console_lock();
3339
3340 deferred_takeover = false;
3341 logo_shown = FBCON_LOGO_DONTSHOW;
3342
3343 fbcon_for_each_registered_fb(i)
3344 do_fb_registered(fbcon_registered_fb[i]);
3345
3346 console_unlock();
3347 }
3348
3349 static struct notifier_block fbcon_output_nb;
3350 static DECLARE_WORK(fbcon_deferred_takeover_work, fbcon_register_existing_fbs);
3351
3352 static int fbcon_output_notifier(struct notifier_block *nb,
3353 unsigned long action, void *data)
3354 {
3355 WARN_CONSOLE_UNLOCKED();
3356
3357 pr_info("fbcon: Taking over console\n");
3358
3359 dummycon_unregister_output_notifier(&fbcon_output_nb);
3360
3361
3362 schedule_work(&fbcon_deferred_takeover_work);
3363
3364 return NOTIFY_OK;
3365 }
3366 #endif
3367
3368 static void fbcon_start(void)
3369 {
3370 WARN_CONSOLE_UNLOCKED();
3371
3372 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3373 if (conswitchp != &dummy_con)
3374 deferred_takeover = false;
3375
3376 if (deferred_takeover) {
3377 fbcon_output_nb.notifier_call = fbcon_output_notifier;
3378 dummycon_register_output_notifier(&fbcon_output_nb);
3379 return;
3380 }
3381 #endif
3382 }
3383
3384 void __init fb_console_init(void)
3385 {
3386 int i;
3387
3388 console_lock();
3389 fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
3390 "fbcon");
3391
3392 if (IS_ERR(fbcon_device)) {
3393 printk(KERN_WARNING "Unable to create device "
3394 "for fbcon; errno = %ld\n",
3395 PTR_ERR(fbcon_device));
3396 fbcon_device = NULL;
3397 } else
3398 fbcon_init_device();
3399
3400 for (i = 0; i < MAX_NR_CONSOLES; i++)
3401 con2fb_map[i] = -1;
3402
3403 fbcon_start();
3404 console_unlock();
3405 }
3406
3407 #ifdef MODULE
3408
3409 static void __exit fbcon_deinit_device(void)
3410 {
3411 int i;
3412
3413 if (fbcon_has_sysfs) {
3414 for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
3415 device_remove_file(fbcon_device, &device_attrs[i]);
3416
3417 fbcon_has_sysfs = 0;
3418 }
3419 }
3420
3421 void __exit fb_console_exit(void)
3422 {
3423 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3424 console_lock();
3425 if (deferred_takeover)
3426 dummycon_unregister_output_notifier(&fbcon_output_nb);
3427 console_unlock();
3428
3429 cancel_work_sync(&fbcon_deferred_takeover_work);
3430 #endif
3431
3432 console_lock();
3433 fbcon_deinit_device();
3434 device_destroy(fb_class, MKDEV(0, 0));
3435
3436 do_unregister_con_driver(&fb_con);
3437 console_unlock();
3438 }
3439 #endif