1/* 2 * linux/drivers/video/fbcon.c -- Low level frame buffer based console driver 3 * 4 * Copyright (C) 1995 Geert Uytterhoeven 5 * 6 * 7 * This file is based on the original Amiga console driver (amicon.c): 8 * 9 * Copyright (C) 1993 Hamish Macdonald 10 * Greg Harp 11 * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk] 12 * 13 * with work by William Rucklidge (wjr@cs.cornell.edu) 14 * Geert Uytterhoeven 15 * Jes Sorensen (jds@kom.auc.dk) 16 * Martin Apel 17 * 18 * and on the original Atari console driver (atacon.c): 19 * 20 * Copyright (C) 1993 Bjoern Brauel 21 * Roman Hodek 22 * 23 * with work by Guenther Kelleter 24 * Martin Schaller 25 * Andreas Schwab 26 * 27 * Hardware cursor support added by Emmanuel Marty (core@ggi-project.org) 28 * Smart redraw scrolling, arbitrary font width support, 512char font support 29 * and software scrollback added by 30 * Jakub Jelinek (jj@ultra.linux.cz) 31 * 32 * Random hacking by Martin Mares <mj@ucw.cz> 33 * 34 * 2001 - Documented with DocBook 35 * - Brad Douglas <brad@neruo.com> 36 * 37 * The low level operations for the various display memory organizations are 38 * now in separate source files. 39 * 40 * Currently the following organizations are supported: 41 * 42 * o afb Amiga bitplanes 43 * o cfb{2,4,8,16,24,32} Packed pixels 44 * o ilbm Amiga interleaved bitplanes 45 * o iplan2p[248] Atari interleaved bitplanes 46 * o mfb Monochrome 47 * o vga VGA characters/attributes 48 * 49 * To do: 50 * 51 * - Implement 16 plane mode (iplan2p16) 52 * 53 * 54 * This file is subject to the terms and conditions of the GNU General Public 55 * License. See the file COPYING in the main directory of this archive for 56 * more details. 57 */ 58 59#undef FBCONDEBUG 60 61#include <linux/config.h> 62#include <linux/module.h> 63#include <linux/types.h> 64#include <linux/sched.h> 65#include <linux/fs.h> 66#include <linux/kernel.h> 67#include <linux/delay.h> /* MSch: for IRQ probe */ 68#include <linux/tty.h> 69#include <linux/console.h> 70#include <linux/string.h> 71#include <linux/kd.h> 72#include <linux/slab.h> 73#include <linux/fb.h> 74#include <linux/vt_kern.h> 75#include <linux/selection.h> 76#include <linux/smp.h> 77#include <linux/init.h> 78#include <linux/pm.h> 79 80#include <asm/irq.h> 81#include <asm/system.h> 82#include <asm/uaccess.h> 83#ifdef CONFIG_AMIGA 84#include <asm/amigahw.h> 85#include <asm/amigaints.h> 86#endif /* CONFIG_AMIGA */ 87#ifdef CONFIG_ATARI 88#include <asm/atariints.h> 89#endif 90#ifdef CONFIG_MAC 91#include <asm/macints.h> 92#endif 93#if defined(__mc68000__) || defined(CONFIG_APUS) 94#include <asm/machdep.h> 95#include <asm/setup.h> 96#endif 97#ifdef CONFIG_FBCON_VGA_PLANES 98#include <asm/io.h> 99#endif 100#define INCLUDE_LINUX_LOGO_DATA 101#include <asm/linux_logo.h> 102 103#include <video/fbcon.h> 104#include <video/fbcon-mac.h> /* for 6x11 font on mac */ 105#include <video/font.h> 106 107#ifdef FBCONDEBUG 108# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) 109#else 110# define DPRINTK(fmt, args...) 111#endif 112 113#define LOGO_H 80 114#define LOGO_W 80 115#define LOGO_LINE (LOGO_W/8) 116 117struct display fb_display[MAX_NR_CONSOLES]; 118char con2fb_map[MAX_NR_CONSOLES]; 119static int logo_lines; 120static int logo_shown = -1; 121/* Software scrollback */ 122int fbcon_softback_size = 32768; 123static unsigned long softback_buf, softback_curr; 124static unsigned long softback_in; 125static unsigned long softback_top, softback_end; 126static int softback_lines; 127 128#define REFCOUNT(fd) (((int *)(fd))[-1]) 129#define FNTSIZE(fd) (((int *)(fd))[-2]) 130#define FNTCHARCNT(fd) (((int *)(fd))[-3]) 131#define FNTSUM(fd) (((int *)(fd))[-4]) 132#define FONT_EXTRA_WORDS 4 133 134#define CM_SOFTBACK (8) 135 136#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * conp->vc_size_row) 137 138static void fbcon_free_font(struct display *); 139static int fbcon_set_origin(struct vc_data *); 140 141#ifdef CONFIG_PM 142static int pm_fbcon_request(struct pm_dev *dev, pm_request_t rqst, void *data); 143static struct pm_dev *pm_fbcon; 144static int fbcon_sleeping; 145#endif 146 147/* 148 * Emmanuel: fbcon will now use a hardware cursor if the 149 * low-level driver provides a non-NULL dispsw->cursor pointer, 150 * in which case the hardware should do blinking, etc. 151 * 152 * if dispsw->cursor is NULL, use Atari alike software cursor 153 */ 154 155static int cursor_drawn; 156 157#define CURSOR_DRAW_DELAY (1) 158 159/* # VBL ints between cursor state changes */ 160#define ARM_CURSOR_BLINK_RATE (10) 161#define AMIGA_CURSOR_BLINK_RATE (20) 162#define ATARI_CURSOR_BLINK_RATE (42) 163#define MAC_CURSOR_BLINK_RATE (32) 164#define DEFAULT_CURSOR_BLINK_RATE (20) 165 166static int vbl_cursor_cnt; 167static int cursor_on; 168static int cursor_blink_rate; 169 170static inline void cursor_undrawn(void) 171{ 172 vbl_cursor_cnt = 0; 173 cursor_drawn = 0; 174} 175 176 177#define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1) 178 179 180/* 181 * Interface used by the world 182 */ 183 184static const char *fbcon_startup(void); 185static void fbcon_init(struct vc_data *conp, int init); 186static void fbcon_deinit(struct vc_data *conp); 187static int fbcon_changevar(int con); 188static void fbcon_clear(struct vc_data *conp, int sy, int sx, int height, 189 int width); 190static void fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos); 191static void fbcon_putcs(struct vc_data *conp, const unsigned short *s, int count, 192 int ypos, int xpos); 193static void fbcon_cursor(struct vc_data *conp, int mode); 194static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir, 195 int count); 196static void fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, 197 int height, int width); 198static int fbcon_switch(struct vc_data *conp); 199static int fbcon_blank(struct vc_data *conp, int blank); 200static int fbcon_font_op(struct vc_data *conp, struct console_font_op *op); 201static int fbcon_set_palette(struct vc_data *conp, unsigned char *table); 202static int fbcon_scrolldelta(struct vc_data *conp, int lines); 203 204 205/* 206 * Internal routines 207 */ 208 209static void fbcon_setup(int con, int init, int logo); 210static __inline__ int real_y(struct display *p, int ypos); 211static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp); 212static __inline__ void updatescrollmode(struct display *p); 213static __inline__ void ywrap_up(int unit, struct vc_data *conp, 214 struct display *p, int count); 215static __inline__ void ywrap_down(int unit, struct vc_data *conp, 216 struct display *p, int count); 217static __inline__ void ypan_up(int unit, struct vc_data *conp, 218 struct display *p, int count); 219static __inline__ void ypan_down(int unit, struct vc_data *conp, 220 struct display *p, int count); 221static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx, 222 int height, int width, u_int y_break); 223 224static int fbcon_show_logo(void); 225 226#ifdef CONFIG_MAC 227/* 228 * On the Macintoy, there may or may not be a working VBL int. We need to probe 229 */ 230static int vbl_detected; 231 232static void fbcon_vbl_detect(int irq, void *dummy, struct pt_regs *fp) 233{ 234 vbl_detected++; 235} 236#endif 237 238static void cursor_timer_handler(unsigned long dev_addr); 239 240static struct timer_list cursor_timer = { 241 function: cursor_timer_handler 242}; 243static int use_timer_cursor; 244 245static void cursor_timer_handler(unsigned long dev_addr) 246{ 247 fbcon_vbl_handler(0, NULL, NULL); 248 cursor_timer.expires = jiffies+HZ/50; 249 add_timer(&cursor_timer); 250} 251 252 253/** 254 * PROC_CONSOLE - find the attached tty or visible console 255 * @info: frame buffer info structure 256 * 257 * Finds the tty attached to the process or visible console if 258 * the process is not directly attached to a tty (e.g. remote 259 * user) for device @info. 260 * 261 * Returns -1 errno on error, or tty/visible console number 262 * on success. 263 * 264 */ 265 266int PROC_CONSOLE(const struct fb_info *info) 267{ 268 int fgc; 269 270 if (info->display_fg != NULL) 271 fgc = info->display_fg->vc_num; 272 else 273 return -1; 274 275 if (!current->tty) 276 return fgc; 277 278 if (current->tty->driver.type != TTY_DRIVER_TYPE_CONSOLE) 279 return fgc; 280 281 if (MINOR(current->tty->device) < 1) 282 return fgc; 283 284 return MINOR(current->tty->device) - 1; 285} 286 287 288/** 289 * set_all_vcs - set all virtual consoles to match 290 * @fbidx: frame buffer index (e.g. fb0, fb1, ...) 291 * @fb: frame buffer ops structure 292 * @var: frame buffer screen structure to set 293 * @info: frame buffer info structure 294 * 295 * Set all virtual consoles to match screen info set in @var 296 * for device @info. 297 * 298 * Returns negative errno on error, or zero on success. 299 * 300 */ 301 302int set_all_vcs(int fbidx, struct fb_ops *fb, struct fb_var_screeninfo *var, 303 struct fb_info *info) 304{ 305 int unit, err; 306 307 var->activate |= FB_ACTIVATE_TEST; 308 err = fb->fb_set_var(var, PROC_CONSOLE(info), info); 309 var->activate &= ~FB_ACTIVATE_TEST; 310 if (err) 311 return err; 312 for (unit = 0; unit < MAX_NR_CONSOLES; unit++) 313 if (fb_display[unit].conp && con2fb_map[unit] == fbidx) 314 fb->fb_set_var(var, unit, info); 315 return 0; 316} 317 318 319/** 320 * set_con2fb_map - map console to frame buffer device 321 * @unit: virtual console number to map 322 * @newidx: frame buffer index to map virtual console to 323 * 324 * Maps a virtual console @unit to a frame buffer device 325 * @newidx. 326 * 327 */ 328 329void set_con2fb_map(int unit, int newidx) 330{ 331 int oldidx = con2fb_map[unit]; 332 struct fb_info *oldfb, *newfb; 333 struct vc_data *conp; 334 char *fontdata; 335 unsigned short fontwidth, fontheight, fontwidthlog, fontheightlog; 336 int userfont; 337 338 if (newidx != con2fb_map[unit]) { 339 oldfb = registered_fb[oldidx]; 340 newfb = registered_fb[newidx]; 341 if (newfb->fbops->owner) 342 __MOD_INC_USE_COUNT(newfb->fbops->owner); 343 if (newfb->fbops->fb_open && newfb->fbops->fb_open(newfb,0)) { 344 if (newfb->fbops->owner) 345 __MOD_DEC_USE_COUNT(newfb->fbops->owner); 346 return; 347 } 348 if (oldfb->fbops->fb_release) 349 oldfb->fbops->fb_release(oldfb,0); 350 if (oldfb->fbops->owner) 351 __MOD_DEC_USE_COUNT(oldfb->fbops->owner); 352 conp = fb_display[unit].conp; 353 fontdata = fb_display[unit].fontdata; 354 fontwidth = fb_display[unit]._fontwidth; 355 fontheight = fb_display[unit]._fontheight; 356 fontwidthlog = fb_display[unit]._fontwidthlog; 357 fontheightlog = fb_display[unit]._fontheightlog; 358 userfont = fb_display[unit].userfont; 359 con2fb_map[unit] = newidx; 360 fb_display[unit] = *(newfb->disp); 361 fb_display[unit].conp = conp; 362 fb_display[unit].fontdata = fontdata; 363 fb_display[unit]._fontwidth = fontwidth; 364 fb_display[unit]._fontheight = fontheight; 365 fb_display[unit]._fontwidthlog = fontwidthlog; 366 fb_display[unit]._fontheightlog = fontheightlog; 367 fb_display[unit].userfont = userfont; 368 fb_display[unit].fb_info = newfb; 369 if (conp) 370 conp->vc_display_fg = &newfb->display_fg; 371 if (!newfb->display_fg) 372 newfb->display_fg = conp; 373 if (!newfb->changevar) 374 newfb->changevar = oldfb->changevar; 375 /* tell console var has changed */ 376 if (newfb->changevar) 377 newfb->changevar(unit); 378 } 379} 380 381/* 382 * Low Level Operations 383 */ 384 385struct display_switch fbcon_dummy; 386 387/* NOTE: fbcon cannot be __init: it may be called from take_over_console later */ 388 389static const char *fbcon_startup(void) 390{ 391 const char *display_desc = "frame buffer device"; 392 int irqres = 1; 393 static int done = 0; 394 395 /* 396 * If num_registered_fb is zero, this is a call for the dummy part. 397 * The frame buffer devices weren't initialized yet. 398 */ 399 if (!num_registered_fb || done) 400 return display_desc; 401 done = 1; 402 403#ifdef CONFIG_AMIGA 404 if (MACH_IS_AMIGA) { 405 cursor_blink_rate = AMIGA_CURSOR_BLINK_RATE; 406 irqres = request_irq(IRQ_AMIGA_VERTB, fbcon_vbl_handler, 0, 407 "console/cursor", fbcon_vbl_handler); 408 } 409#endif /* CONFIG_AMIGA */ 410#ifdef CONFIG_ATARI 411 if (MACH_IS_ATARI) { 412 cursor_blink_rate = ATARI_CURSOR_BLINK_RATE; 413 irqres = request_irq(IRQ_AUTO_4, fbcon_vbl_handler, IRQ_TYPE_PRIO, 414 "console/cursor", fbcon_vbl_handler); 415 } 416#endif /* CONFIG_ATARI */ 417 418#ifdef CONFIG_MAC 419 /* 420 * On a Macintoy, the VBL interrupt may or may not be active. 421 * As interrupt based cursor is more reliable and race free, we 422 * probe for VBL interrupts. 423 */ 424 if (MACH_IS_MAC) { 425 int ct = 0; 426 /* 427 * Probe for VBL: set temp. handler ... 428 */ 429 irqres = request_irq(IRQ_MAC_VBL, fbcon_vbl_detect, 0, 430 "console/cursor", fbcon_vbl_detect); 431 vbl_detected = 0; 432 433 /* 434 * ... and spin for 20 ms ... 435 */ 436 while (!vbl_detected && ++ct<1000) 437 udelay(20); 438 439 if(ct==1000) 440 printk("fbcon_startup: No VBL detected, using timer based cursor.\n"); 441 442 free_irq(IRQ_MAC_VBL, fbcon_vbl_detect); 443 444 if (vbl_detected) { 445 /* 446 * interrupt based cursor ok 447 */ 448 cursor_blink_rate = MAC_CURSOR_BLINK_RATE; 449 irqres = request_irq(IRQ_MAC_VBL, fbcon_vbl_handler, 0, 450 "console/cursor", fbcon_vbl_handler); 451 } else { 452 /* 453 * VBL not detected: fall through, use timer based cursor 454 */ 455 irqres = 1; 456 } 457 } 458#endif /* CONFIG_MAC */ 459 460#if defined(__arm__) && defined(IRQ_VSYNCPULSE) 461 cursor_blink_rate = ARM_CURSOR_BLINK_RATE; 462 irqres = request_irq(IRQ_VSYNCPULSE, fbcon_vbl_handler, SA_SHIRQ, 463 "console/cursor", fbcon_vbl_handler); 464#endif 465 466 if (irqres) { 467 use_timer_cursor = 1; 468 cursor_blink_rate = DEFAULT_CURSOR_BLINK_RATE; 469 cursor_timer.expires = jiffies+HZ/50; 470 add_timer(&cursor_timer); 471 } 472 473#ifdef CONFIG_PM 474 pm_fbcon = pm_register(PM_SYS_DEV, PM_SYS_VGA, pm_fbcon_request); 475#endif 476 477 return display_desc; 478} 479 480 481static void fbcon_init(struct vc_data *conp, int init) 482{ 483 int unit = conp->vc_num; 484 struct fb_info *info; 485 486 /* on which frame buffer will we open this console? */ 487 info = registered_fb[(int)con2fb_map[unit]]; 488 489 info->changevar = &fbcon_changevar; 490 fb_display[unit] = *(info->disp); /* copy from default */ 491 DPRINTK("mode: %s\n",info->modename); 492 DPRINTK("visual: %d\n",fb_display[unit].visual); 493 DPRINTK("res: %dx%d-%d\n",fb_display[unit].var.xres, 494 fb_display[unit].var.yres, 495 fb_display[unit].var.bits_per_pixel); 496 fb_display[unit].conp = conp; 497 fb_display[unit].fb_info = info; 498 /* clear out the cmap so we don't have dangling pointers */ 499 fb_display[unit].cmap.len = 0; 500 fb_display[unit].cmap.red = 0; 501 fb_display[unit].cmap.green = 0; 502 fb_display[unit].cmap.blue = 0; 503 fb_display[unit].cmap.transp = 0; 504 fbcon_setup(unit, init, !init); 505 /* Must be done after fbcon_setup to prevent excess updates */ 506 conp->vc_display_fg = &info->display_fg; 507 if (!info->display_fg) 508 info->display_fg = conp; 509} 510 511 512static void fbcon_deinit(struct vc_data *conp) 513{ 514 int unit = conp->vc_num; 515 struct display *p = &fb_display[unit]; 516 517 fbcon_free_font(p); 518 p->dispsw = &fbcon_dummy; 519 p->conp = 0; 520} 521 522 523static int fbcon_changevar(int con) 524{ 525 if (fb_display[con].conp) 526 fbcon_setup(con, 0, 0); 527 return 0; 528} 529 530 531static __inline__ void updatescrollmode(struct display *p) 532{ 533 int m; 534 if (p->scrollmode & __SCROLL_YFIXED) 535 return; 536 if (divides(p->ywrapstep, fontheight(p)) && 537 divides(fontheight(p), p->var.yres_virtual)) 538 m = __SCROLL_YWRAP; 539 else if (divides(p->ypanstep, fontheight(p)) && 540 p->var.yres_virtual >= p->var.yres+fontheight(p)) 541 m = __SCROLL_YPAN; 542 else if (p->scrollmode & __SCROLL_YNOMOVE) 543 m = __SCROLL_YREDRAW; 544 else 545 m = __SCROLL_YMOVE; 546 p->scrollmode = (p->scrollmode & ~__SCROLL_YMASK) | m; 547} 548 549static void fbcon_font_widths(struct display *p) 550{ 551 int i; 552 553 p->_fontwidthlog = 0; 554 for (i = 2; i <= 6; i++) 555 if (fontwidth(p) == (1 << i)) 556 p->_fontwidthlog = i; 557 p->_fontheightlog = 0; 558 for (i = 2; i <= 6; i++) 559 if (fontheight(p) == (1 << i)) 560 p->_fontheightlog = i; 561} 562 563#define fontwidthvalid(p,w) ((p)->dispsw->fontwidthmask & FONTWIDTH(w)) 564 565static void fbcon_setup(int con, int init, int logo) 566{ 567 struct display *p = &fb_display[con]; 568 struct vc_data *conp = p->conp; 569 int nr_rows, nr_cols; 570 int old_rows, old_cols; 571 unsigned short *save = NULL, *r, *q; 572 int i, charcnt = 256; 573 struct fbcon_font_desc *font; 574 575 if (con != fg_console || (p->fb_info->flags & FBINFO_FLAG_MODULE) || 576 p->type == FB_TYPE_TEXT) 577 logo = 0; 578 579 p->var.xoffset = p->var.yoffset = p->yscroll = 0; /* reset wrap/pan */ 580 581 if (con == fg_console && p->type != FB_TYPE_TEXT) { 582 if (fbcon_softback_size) { 583 if (!softback_buf) { 584 softback_buf = (unsigned long)kmalloc(fbcon_softback_size, GFP_KERNEL); 585 if (!softback_buf) { 586 fbcon_softback_size = 0; 587 softback_top = 0; 588 } 589 } 590 } else { 591 if (softback_buf) { 592 kfree((void *)softback_buf); 593 softback_buf = 0; 594 softback_top = 0; 595 } 596 } 597 if (softback_buf) 598 softback_in = softback_top = softback_curr = softback_buf; 599 softback_lines = 0; 600 } 601 602 for (i = 0; i < MAX_NR_CONSOLES; i++) 603 if (i != con && fb_display[i].fb_info == p->fb_info && 604 fb_display[i].conp && fb_display[i].fontdata) 605 break; 606 607 fbcon_free_font(p); 608 if (i < MAX_NR_CONSOLES) { 609 struct display *q = &fb_display[i]; 610 611 if (fontwidthvalid(p,fontwidth(q))) { 612 /* If we are not the first console on this 613 fb, copy the font from that console */ 614 p->_fontwidth = q->_fontwidth; 615 p->_fontheight = q->_fontheight; 616 p->_fontwidthlog = q->_fontwidthlog; 617 p->_fontheightlog = q->_fontheightlog; 618 p->fontdata = q->fontdata; 619 p->userfont = q->userfont; 620 if (p->userfont) { 621 REFCOUNT(p->fontdata)++; 622 charcnt = FNTCHARCNT(p->fontdata); 623 } 624 con_copy_unimap(con, i); 625 } 626 } 627 628 if (!p->fontdata) { 629 if (!p->fb_info->fontname[0] || 630 !(font = fbcon_find_font(p->fb_info->fontname))) 631 font = fbcon_get_default_font(p->var.xres, p->var.yres); 632 p->_fontwidth = font->width; 633 p->_fontheight = font->height; 634 p->fontdata = font->data; 635 fbcon_font_widths(p); 636 } 637 638 if (!fontwidthvalid(p,fontwidth(p))) { 639#if defined(CONFIG_FBCON_MAC) && defined(CONFIG_MAC) 640 if (MACH_IS_MAC) 641 /* ++Geert: hack to make 6x11 fonts work on mac */ 642 p->dispsw = &fbcon_mac; 643 else 644#endif 645 { 646 /* ++Geert: changed from panic() to `correct and continue' */ 647 printk(KERN_ERR "fbcon_setup: No support for fontwidth %d\n", fontwidth(p)); 648 p->dispsw = &fbcon_dummy; 649 } 650 } 651 if (p->dispsw->set_font) 652 p->dispsw->set_font(p, fontwidth(p), fontheight(p)); 653 updatescrollmode(p); 654 655 old_cols = conp->vc_cols; 656 old_rows = conp->vc_rows; 657 658 nr_cols = p->var.xres/fontwidth(p); 659 nr_rows = p->var.yres/fontheight(p); 660 661 if (logo) { 662 /* Need to make room for the logo */ 663 int cnt; 664 int step; 665 666 logo_lines = (LOGO_H + fontheight(p) - 1) / fontheight(p); 667 q = (unsigned short *)(conp->vc_origin + conp->vc_size_row * old_rows); 668 step = logo_lines * old_cols; 669 for (r = q - logo_lines * old_cols; r < q; r++) 670 if (scr_readw(r) != conp->vc_video_erase_char) 671 break; 672 if (r != q && nr_rows >= old_rows + logo_lines) { 673 save = kmalloc(logo_lines * nr_cols * 2, GFP_KERNEL); 674 if (save) { 675 int i = old_cols < nr_cols ? old_cols : nr_cols; 676 scr_memsetw(save, conp->vc_video_erase_char, logo_lines * nr_cols * 2); 677 r = q - step; 678 for (cnt = 0; cnt < logo_lines; cnt++, r += i) 679 scr_memcpyw(save + cnt * nr_cols, r, 2 * i); 680 r = q; 681 } 682 } 683 if (r == q) { 684 /* We can scroll screen down */ 685 r = q - step - old_cols; 686 for (cnt = old_rows - logo_lines; cnt > 0; cnt--) { 687 scr_memcpyw(r + step, r, conp->vc_size_row); 688 r -= old_cols; 689 } 690 if (!save) { 691 conp->vc_y += logo_lines; 692 conp->vc_pos += logo_lines * conp->vc_size_row; 693 } 694 } 695 scr_memsetw((unsigned short *)conp->vc_origin, 696 conp->vc_video_erase_char, 697 conp->vc_size_row * logo_lines); 698 } 699 700 /* 701 * ++guenther: console.c:vc_allocate() relies on initializing 702 * vc_{cols,rows}, but we must not set those if we are only 703 * resizing the console. 704 */ 705 if (init) { 706 conp->vc_cols = nr_cols; 707 conp->vc_rows = nr_rows; 708 } 709 p->vrows = p->var.yres_virtual/fontheight(p); 710 if ((p->var.yres % fontheight(p)) && 711 (p->var.yres_virtual % fontheight(p) < p->var.yres % fontheight(p))) 712 p->vrows--; 713 conp->vc_can_do_color = p->var.bits_per_pixel != 1; 714 conp->vc_complement_mask = conp->vc_can_do_color ? 0x7700 : 0x0800; 715 if (charcnt == 256) { 716 conp->vc_hi_font_mask = 0; 717 p->fgshift = 8; 718 p->bgshift = 12; 719 p->charmask = 0xff; 720 } else { 721 conp->vc_hi_font_mask = 0x100; 722 if (conp->vc_can_do_color) 723 conp->vc_complement_mask <<= 1; 724 p->fgshift = 9; 725 p->bgshift = 13; 726 p->charmask = 0x1ff; 727 } 728 729 if (p->dispsw == &fbcon_dummy) 730 printk(KERN_WARNING "fbcon_setup: type %d (aux %d, depth %d) not " 731 "supported\n", p->type, p->type_aux, p->var.bits_per_pixel); 732 p->dispsw->setup(p); 733 734 p->fgcol = p->var.bits_per_pixel > 2 ? 7 : (1<<p->var.bits_per_pixel)-1; 735 p->bgcol = 0; 736 737 if (!init) { 738 if (conp->vc_cols != nr_cols || conp->vc_rows != nr_rows) 739 vc_resize_con(nr_rows, nr_cols, con); 740 else if (CON_IS_VISIBLE(conp) && 741 vt_cons[conp->vc_num]->vc_mode == KD_TEXT) { 742 if (p->dispsw->clear_margins) 743 p->dispsw->clear_margins(conp, p, 0); 744 update_screen(con); 745 } 746 if (save) { 747 q = (unsigned short *)(conp->vc_origin + conp->vc_size_row * old_rows); 748 scr_memcpyw(q, save, logo_lines * nr_cols * 2); 749 conp->vc_y += logo_lines; 750 conp->vc_pos += logo_lines * conp->vc_size_row; 751 kfree(save); 752 } 753 } 754 755 if (logo) { 756 logo_shown = -2; 757 conp->vc_top = logo_lines; 758 } 759 760 if (con == fg_console && softback_buf) { 761 int l = fbcon_softback_size / conp->vc_size_row; 762 if (l > 5) 763 softback_end = softback_buf + l * conp->vc_size_row; 764 else { 765 /* Smaller scrollback makes no sense, and 0 would screw 766 the operation totally */ 767 softback_top = 0; 768 } 769 } 770} 771 772 773/* ====================================================================== */ 774 775/* fbcon_XXX routines - interface used by the world 776 * 777 * This system is now divided into two levels because of complications 778 * caused by hardware scrolling. Top level functions: 779 * 780 * fbcon_bmove(), fbcon_clear(), fbcon_putc() 781 * 782 * handles y values in range [0, scr_height-1] that correspond to real 783 * screen positions. y_wrap shift means that first line of bitmap may be 784 * anywhere on this display. These functions convert lineoffsets to 785 * bitmap offsets and deal with the wrap-around case by splitting blits. 786 * 787 * fbcon_bmove_physical_8() -- These functions fast implementations 788 * fbcon_clear_physical_8() -- of original fbcon_XXX fns. 789 * fbcon_putc_physical_8() -- (fontwidth != 8) may be added later 790 * 791 * WARNING: 792 * 793 * At the moment fbcon_putc() cannot blit across vertical wrap boundary 794 * Implies should only really hardware scroll in rows. Only reason for 795 * restriction is simplicity & efficiency at the moment. 796 */ 797 798static __inline__ int real_y(struct display *p, int ypos) 799{ 800 int rows = p->vrows; 801 802 ypos += p->yscroll; 803 return ypos < rows ? ypos : ypos-rows; 804} 805 806 807static void fbcon_clear(struct vc_data *conp, int sy, int sx, int height, 808 int width) 809{ 810 int unit = conp->vc_num; 811 struct display *p = &fb_display[unit]; 812 u_int y_break; 813 int redraw_cursor = 0; 814 815 if (!p->can_soft_blank && console_blanked) 816 return; 817 818 if (!height || !width) 819 return; 820 821 if ((sy <= p->cursor_y) && (p->cursor_y < sy+height) && 822 (sx <= p->cursor_x) && (p->cursor_x < sx+width)) { 823 cursor_undrawn(); 824 redraw_cursor = 1; 825 } 826 827 /* Split blits that cross physical y_wrap boundary */ 828 829 y_break = p->vrows-p->yscroll; 830 if (sy < y_break && sy+height-1 >= y_break) { 831 u_int b = y_break-sy; 832 p->dispsw->clear(conp, p, real_y(p, sy), sx, b, width); 833 p->dispsw->clear(conp, p, real_y(p, sy+b), sx, height-b, width); 834 } else 835 p->dispsw->clear(conp, p, real_y(p, sy), sx, height, width); 836 837 if (redraw_cursor) 838 vbl_cursor_cnt = CURSOR_DRAW_DELAY; 839} 840 841 842static void fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos) 843{ 844 int unit = conp->vc_num; 845 struct display *p = &fb_display[unit]; 846 int redraw_cursor = 0; 847 848 if (!p->can_soft_blank && console_blanked) 849 return; 850 851 if (vt_cons[unit]->vc_mode != KD_TEXT) 852 return; 853 854 if ((p->cursor_x == xpos) && (p->cursor_y == ypos)) { 855 cursor_undrawn(); 856 redraw_cursor = 1; 857 } 858 859 p->dispsw->putc(conp, p, c, real_y(p, ypos), xpos); 860 861 if (redraw_cursor) 862 vbl_cursor_cnt = CURSOR_DRAW_DELAY; 863} 864 865 866static void fbcon_putcs(struct vc_data *conp, const unsigned short *s, int count, 867 int ypos, int xpos) 868{ 869 int unit = conp->vc_num; 870 struct display *p = &fb_display[unit]; 871 int redraw_cursor = 0; 872 873 if (!p->can_soft_blank && console_blanked) 874 return; 875 876 if (vt_cons[unit]->vc_mode != KD_TEXT) 877 return; 878 879 if ((p->cursor_y == ypos) && (xpos <= p->cursor_x) && 880 (p->cursor_x < (xpos + count))) { 881 cursor_undrawn(); 882 redraw_cursor = 1; 883 } 884 p->dispsw->putcs(conp, p, s, count, real_y(p, ypos), xpos); 885 if (redraw_cursor) 886 vbl_cursor_cnt = CURSOR_DRAW_DELAY; 887} 888 889 890static void fbcon_cursor(struct vc_data *conp, int mode) 891{ 892 int unit = conp->vc_num; 893 struct display *p = &fb_display[unit]; 894 int y = conp->vc_y; 895 896 if (mode & CM_SOFTBACK) { 897 mode &= ~CM_SOFTBACK; 898 if (softback_lines) { 899 if (y + softback_lines >= conp->vc_rows) 900 mode = CM_ERASE; 901 else 902 y += softback_lines; 903 } 904 } else if (softback_lines) 905 fbcon_set_origin(conp); 906 907 /* do we have a hardware cursor ? */ 908 if (p->dispsw->cursor) { 909 p->cursor_x = conp->vc_x; 910 p->cursor_y = y; 911 p->dispsw->cursor(p, mode, p->cursor_x, real_y(p, p->cursor_y)); 912 return; 913 } 914 915 /* Avoid flickering if there's no real change. */ 916 if (p->cursor_x == conp->vc_x && p->cursor_y == y && 917 (mode == CM_ERASE) == !cursor_on) 918 return; 919 920 cursor_on = 0; 921 if (cursor_drawn) 922 p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y)); 923 924 p->cursor_x = conp->vc_x; 925 p->cursor_y = y; 926 927 switch (mode) { 928 case CM_ERASE: 929 cursor_drawn = 0; 930 break; 931 case CM_MOVE: 932 case CM_DRAW: 933 if (cursor_drawn) 934 p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y)); 935 vbl_cursor_cnt = CURSOR_DRAW_DELAY; 936 cursor_on = 1; 937 break; 938 } 939} 940 941 942static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp) 943{ 944 struct display *p; 945 946 if (!cursor_on) 947 return; 948 949 if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) { 950 p = &fb_display[fg_console]; 951 if (p->dispsw->revc) 952 p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y)); 953 cursor_drawn ^= 1; 954 vbl_cursor_cnt = cursor_blink_rate; 955 } 956} 957 958static int scrollback_phys_max = 0; 959static int scrollback_max = 0; 960static int scrollback_current = 0; 961 962static __inline__ void ywrap_up(int unit, struct vc_data *conp, 963 struct display *p, int count) 964{ 965 p->yscroll += count; 966 if (p->yscroll >= p->vrows) /* Deal with wrap */ 967 p->yscroll -= p->vrows; 968 p->var.xoffset = 0; 969 p->var.yoffset = p->yscroll*fontheight(p); 970 p->var.vmode |= FB_VMODE_YWRAP; 971 p->fb_info->updatevar(unit, p->fb_info); 972 scrollback_max += count; 973 if (scrollback_max > scrollback_phys_max) 974 scrollback_max = scrollback_phys_max; 975 scrollback_current = 0; 976} 977 978 979static __inline__ void ywrap_down(int unit, struct vc_data *conp, 980 struct display *p, int count) 981{ 982 p->yscroll -= count; 983 if (p->yscroll < 0) /* Deal with wrap */ 984 p->yscroll += p->vrows; 985 p->var.xoffset = 0; 986 p->var.yoffset = p->yscroll*fontheight(p); 987 p->var.vmode |= FB_VMODE_YWRAP; 988 p->fb_info->updatevar(unit, p->fb_info); 989 scrollback_max -= count; 990 if (scrollback_max < 0) 991 scrollback_max = 0; 992 scrollback_current = 0; 993} 994 995 996static __inline__ void ypan_up(int unit, struct vc_data *conp, 997 struct display *p, int count) 998{ 999 p->yscroll += count; 1000 if (p->yscroll > p->vrows-conp->vc_rows) { 1001 p->dispsw->bmove(p, p->vrows-conp->vc_rows, 0, 0, 0, 1002 conp->vc_rows, conp->vc_cols); 1003 p->yscroll -= p->vrows-conp->vc_rows; 1004 } 1005 p->var.xoffset = 0; 1006 p->var.yoffset = p->yscroll*fontheight(p); 1007 p->var.vmode &= ~FB_VMODE_YWRAP; 1008 p->fb_info->updatevar(unit, p->fb_info); 1009 if (p->dispsw->clear_margins) 1010 p->dispsw->clear_margins(conp, p, 1); 1011 scrollback_max += count; 1012 if (scrollback_max > scrollback_phys_max) 1013 scrollback_max = scrollback_phys_max; 1014 scrollback_current = 0; 1015} 1016 1017 1018static __inline__ void ypan_down(int unit, struct vc_data *conp, 1019 struct display *p, int count) 1020{ 1021 p->yscroll -= count; 1022 if (p->yscroll < 0) { 1023 p->dispsw->bmove(p, 0, 0, p->vrows-conp->vc_rows, 0, 1024 conp->vc_rows, conp->vc_cols); 1025 p->yscroll += p->vrows-conp->vc_rows; 1026 } 1027 p->var.xoffset = 0; 1028 p->var.yoffset = p->yscroll*fontheight(p); 1029 p->var.vmode &= ~FB_VMODE_YWRAP; 1030 p->fb_info->updatevar(unit, p->fb_info); 1031 if (p->dispsw->clear_margins) 1032 p->dispsw->clear_margins(conp, p, 1); 1033 scrollback_max -= count; 1034 if (scrollback_max < 0) 1035 scrollback_max = 0; 1036 scrollback_current = 0; 1037} 1038 1039static void fbcon_redraw_softback(struct vc_data *conp, struct display *p, long delta) 1040{ 1041 unsigned short *d, *s; 1042 unsigned long n; 1043 int line = 0; 1044 int count = conp->vc_rows; 1045 1046 d = (u16 *)softback_curr; 1047 if (d == (u16 *)softback_in) 1048 d = (u16 *)conp->vc_origin; 1049 n = softback_curr + delta * conp->vc_size_row; 1050 softback_lines -= delta; 1051 if (delta < 0) { 1052 if (softback_curr < softback_top && n < softback_buf) { 1053 n += softback_end - softback_buf; 1054 if (n < softback_top) { 1055 softback_lines -= (softback_top - n) / conp->vc_size_row; 1056 n = softback_top; 1057 } 1058 } else if (softback_curr >= softback_top && n < softback_top) { 1059 softback_lines -= (softback_top - n) / conp->vc_size_row; 1060 n = softback_top; 1061 } 1062 } else { 1063 if (softback_curr > softback_in && n >= softback_end) { 1064 n += softback_buf - softback_end; 1065 if (n > softback_in) { 1066 n = softback_in; 1067 softback_lines = 0; 1068 } 1069 } else if (softback_curr <= softback_in && n > softback_in) { 1070 n = softback_in; 1071 softback_lines = 0; 1072 } 1073 } 1074 if (n == softback_curr) 1075 return; 1076 softback_curr = n; 1077 s = (u16 *)softback_curr; 1078 if (s == (u16 *)softback_in) 1079 s = (u16 *)conp->vc_origin; 1080 while (count--) { 1081 unsigned short *start; 1082 unsigned short *le; 1083 unsigned short c; 1084 int x = 0; 1085 unsigned short attr = 1; 1086 1087 start = s; 1088 le = advance_row(s, 1); 1089 do { 1090 c = scr_readw(s); 1091 if (attr != (c & 0xff00)) { 1092 attr = c & 0xff00; 1093 if (s > start) { 1094 p->dispsw->putcs(conp, p, start, s - start, 1095 real_y(p, line), x); 1096 x += s - start; 1097 start = s; 1098 } 1099 } 1100 if (c == scr_readw(d)) { 1101 if (s > start) { 1102 p->dispsw->putcs(conp, p, start, s - start, 1103 real_y(p, line), x); 1104 x += s - start + 1; 1105 start = s + 1; 1106 } else { 1107 x++; 1108 start++; 1109 } 1110 } 1111 s++; 1112 d++; 1113 } while (s < le); 1114 if (s > start) 1115 p->dispsw->putcs(conp, p, start, s - start, real_y(p, line), x); 1116 line++; 1117 if (d == (u16 *)softback_end) 1118 d = (u16 *)softback_buf; 1119 if (d == (u16 *)softback_in) 1120 d = (u16 *)conp->vc_origin; 1121 if (s == (u16 *)softback_end) 1122 s = (u16 *)softback_buf; 1123 if (s == (u16 *)softback_in) 1124 s = (u16 *)conp->vc_origin; 1125 } 1126} 1127 1128static void fbcon_redraw(struct vc_data *conp, struct display *p, 1129 int line, int count, int offset) 1130{ 1131 unsigned short *d = (unsigned short *) 1132 (conp->vc_origin + conp->vc_size_row * line); 1133 unsigned short *s = d + offset; 1134 1135 while (count--) { 1136 unsigned short *start = s; 1137 unsigned short *le = advance_row(s, 1); 1138 unsigned short c; 1139 int x = 0; 1140 unsigned short attr = 1; 1141 1142 do { 1143 c = scr_readw(s); 1144 if (attr != (c & 0xff00)) { 1145 attr = c & 0xff00; 1146 if (s > start) { 1147 p->dispsw->putcs(conp, p, start, s - start, 1148 real_y(p, line), x); 1149 x += s - start; 1150 start = s; 1151 } 1152 } 1153 if (c == scr_readw(d)) { 1154 if (s > start) { 1155 p->dispsw->putcs(conp, p, start, s - start, 1156 real_y(p, line), x); 1157 x += s - start + 1; 1158 start = s + 1; 1159 } else { 1160 x++; 1161 start++; 1162 } 1163 } 1164 scr_writew(c, d); 1165 console_conditional_schedule(); 1166 s++; 1167 d++; 1168 } while (s < le); 1169 if (s > start) 1170 p->dispsw->putcs(conp, p, start, s - start, real_y(p, line), x); 1171 console_conditional_schedule(); 1172 if (offset > 0) 1173 line++; 1174 else { 1175 line--; 1176 /* NOTE: We subtract two lines from these pointers */ 1177 s -= conp->vc_size_row; 1178 d -= conp->vc_size_row; 1179 } 1180 } 1181} 1182 1183/** 1184 * fbcon_redraw_clear - clear area of the screen 1185 * @conp: stucture pointing to current active virtual console 1186 * @p: display structure 1187 * @sy: starting Y coordinate 1188 * @sx: starting X coordinate 1189 * @height: height of area to clear 1190 * @width: width of area to clear 1191 * 1192 * Clears a specified area of the screen. All dimensions are in 1193 * pixels. 1194 * 1195 */ 1196 1197void fbcon_redraw_clear(struct vc_data *conp, struct display *p, int sy, int sx, 1198 int height, int width) 1199{ 1200 int x, y; 1201 for (y=0; y<height; y++) 1202 for (x=0; x<width; x++) 1203 fbcon_putc(conp, ' ', sy+y, sx+x); 1204} 1205 1206 1207/** 1208 * fbcon_redraw_bmove - copy area of screen to another area 1209 * @p: display structure 1210 * @sy: origin Y coordinate 1211 * @sx: origin X coordinate 1212 * @dy: destination Y coordinate 1213 * @dx: destination X coordinate 1214 * @h: height of area to copy 1215 * @w: width of area to copy 1216 * 1217 * Copies an area of the screen to another area of the same screen. 1218 * All dimensions are in pixels. 1219 * 1220 * Note that this function cannot be used together with ypan or 1221 * ywrap. 1222 * 1223 */ 1224 1225void fbcon_redraw_bmove(struct display *p, int sy, int sx, int dy, int dx, int h, int w) 1226{ 1227 if (sy != dy) 1228 panic("fbcon_redraw_bmove width sy != dy"); 1229 /* h will be always 1, but it does not matter if we are more generic */ 1230 1231 while (h-- > 0) { 1232 struct vc_data *conp = p->conp; 1233 unsigned short *d = (unsigned short *) 1234 (conp->vc_origin + conp->vc_size_row * dy + dx * 2); 1235 unsigned short *s = d + (dx - sx); 1236 unsigned short *start = d; 1237 unsigned short *ls = d; 1238 unsigned short *le = d + w; 1239 unsigned short c; 1240 int x = dx; 1241 unsigned short attr = 1; 1242 1243 do { 1244 c = scr_readw(d); 1245 if (attr != (c & 0xff00)) { 1246 attr = c & 0xff00; 1247 if (d > start) { 1248 p->dispsw->putcs(conp, p, start, d - start, dy, x); 1249 x += d - start; 1250 start = d; 1251 } 1252 } 1253 if (s >= ls && s < le && c == scr_readw(s)) { 1254 if (d > start) { 1255 p->dispsw->putcs(conp, p, start, d - start, dy, x); 1256 x += d - start + 1; 1257 start = d + 1; 1258 } else { 1259 x++; 1260 start++; 1261 } 1262 } 1263 s++; 1264 d++; 1265 } while (d < le); 1266 if (d > start) 1267 p->dispsw->putcs(conp, p, start, d - start, dy, x); 1268 sy++; 1269 dy++; 1270 } 1271} 1272 1273static inline void fbcon_softback_note(struct vc_data *conp, int t, int count) 1274{ 1275 unsigned short *p; 1276 1277 if (conp->vc_num != fg_console) 1278 return; 1279 p = (unsigned short *)(conp->vc_origin + t * conp->vc_size_row); 1280 1281 while (count) { 1282 scr_memcpyw((u16 *)softback_in, p, conp->vc_size_row); 1283 count--; 1284 p = advance_row(p, 1); 1285 softback_in += conp->vc_size_row; 1286 if (softback_in == softback_end) 1287 softback_in = softback_buf; 1288 if (softback_in == softback_top) { 1289 softback_top += conp->vc_size_row; 1290 if (softback_top == softback_end) 1291 softback_top = softback_buf; 1292 } 1293 } 1294 softback_curr = softback_in; 1295} 1296 1297static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir, 1298 int count) 1299{ 1300 int unit = conp->vc_num; 1301 struct display *p = &fb_display[unit]; 1302 int scroll_partial = !(p->scrollmode & __SCROLL_YNOPARTIAL); 1303 1304 if (!p->can_soft_blank && console_blanked) 1305 return 0; 1306 1307 if (!count || vt_cons[unit]->vc_mode != KD_TEXT) 1308 return 0; 1309 1310 fbcon_cursor(conp, CM_ERASE); 1311 1312 /* 1313 * ++Geert: Only use ywrap/ypan if the console is in text mode 1314 * ++Andrew: Only use ypan on hardware text mode when scrolling the 1315 * whole screen (prevents flicker). 1316 */ 1317 1318 switch (dir) { 1319 case SM_UP: 1320 if (count > conp->vc_rows) /* Maximum realistic size */ 1321 count = conp->vc_rows; 1322 if (softback_top) 1323 fbcon_softback_note(conp, t, count); 1324 if (logo_shown >= 0) goto redraw_up; 1325 switch (p->scrollmode & __SCROLL_YMASK) { 1326 case __SCROLL_YMOVE: 1327 p->dispsw->bmove(p, t+count, 0, t, 0, b-t-count, 1328 conp->vc_cols); 1329 p->dispsw->clear(conp, p, b-count, 0, count, 1330 conp->vc_cols); 1331 break; 1332 1333 case __SCROLL_YWRAP: 1334 if (b-t-count > 3*conp->vc_rows>>2) { 1335 if (t > 0) 1336 fbcon_bmove(conp, 0, 0, count, 0, t, 1337 conp->vc_cols); 1338 ywrap_up(unit, conp, p, count); 1339 if (conp->vc_rows-b > 0) 1340 fbcon_bmove(conp, b-count, 0, b, 0, 1341 conp->vc_rows-b, conp->vc_cols); 1342 } else if (p->scrollmode & __SCROLL_YPANREDRAW) 1343 goto redraw_up; 1344 else 1345 fbcon_bmove(conp, t+count, 0, t, 0, b-t-count, 1346 conp->vc_cols); 1347 fbcon_clear(conp, b-count, 0, count, conp->vc_cols); 1348 break; 1349 1350 case __SCROLL_YPAN: 1351 if (( p->yscroll + count <= 2 * (p->vrows - conp->vc_rows)) && 1352 (( !scroll_partial && (b-t == conp->vc_rows)) || 1353 ( scroll_partial && (b-t-count > 3*conp->vc_rows>>2)))) { 1354 if (t > 0) 1355 fbcon_bmove(conp, 0, 0, count, 0, t, 1356 conp->vc_cols); 1357 ypan_up(unit, conp, p, count); 1358 if (conp->vc_rows-b > 0) 1359 fbcon_bmove(conp, b-count, 0, b, 0, 1360 conp->vc_rows-b, conp->vc_cols); 1361 } else if (p->scrollmode & __SCROLL_YPANREDRAW) 1362 goto redraw_up; 1363 else 1364 fbcon_bmove(conp, t+count, 0, t, 0, b-t-count, 1365 conp->vc_cols); 1366 fbcon_clear(conp, b-count, 0, count, conp->vc_cols); 1367 break; 1368 1369 case __SCROLL_YREDRAW: 1370 redraw_up: 1371 fbcon_redraw(conp, p, t, b-t-count, count*conp->vc_cols); 1372 p->dispsw->clear(conp, p, real_y(p, b-count), 0, 1373 count, conp->vc_cols); 1374 scr_memsetw((unsigned short *)(conp->vc_origin + 1375 conp->vc_size_row * (b-count)), 1376 conp->vc_video_erase_char, 1377 conp->vc_size_row * count); 1378 return 1; 1379 } 1380 break; 1381 1382 case SM_DOWN: 1383 if (count > conp->vc_rows) /* Maximum realistic size */ 1384 count = conp->vc_rows; 1385 switch (p->scrollmode & __SCROLL_YMASK) { 1386 case __SCROLL_YMOVE: 1387 p->dispsw->bmove(p, t, 0, t+count, 0, b-t-count, 1388 conp->vc_cols); 1389 p->dispsw->clear(conp, p, t, 0, 1390 count, conp->vc_cols); 1391 break; 1392 1393 case __SCROLL_YWRAP: 1394 if (b-t-count > 3*conp->vc_rows>>2) { 1395 if (conp->vc_rows-b > 0) 1396 fbcon_bmove(conp, b, 0, b-count, 0, 1397 conp->vc_rows-b, conp->vc_cols); 1398 ywrap_down(unit, conp, p, count); 1399 if (t > 0) 1400 fbcon_bmove(conp, count, 0, 0, 0, t, 1401 conp->vc_cols); 1402 } else if (p->scrollmode & __SCROLL_YPANREDRAW) 1403 goto redraw_down; 1404 else 1405 fbcon_bmove(conp, t, 0, t+count, 0, b-t-count, 1406 conp->vc_cols); 1407 fbcon_clear(conp, t, 0, count, conp->vc_cols); 1408 break; 1409 1410 case __SCROLL_YPAN: 1411 if (( count-p->yscroll <= p->vrows-conp->vc_rows) && 1412 (( !scroll_partial && (b-t == conp->vc_rows)) || 1413 ( scroll_partial && (b-t-count > 3*conp->vc_rows>>2)))) { 1414 if (conp->vc_rows-b > 0) 1415 fbcon_bmove(conp, b, 0, b-count, 0, 1416 conp->vc_rows-b, conp->vc_cols); 1417 ypan_down(unit, conp, p, count); 1418 if (t > 0) 1419 fbcon_bmove(conp, count, 0, 0, 0, t, 1420 conp->vc_cols); 1421 } else if (p->scrollmode & __SCROLL_YPANREDRAW) 1422 goto redraw_down; 1423 else 1424 fbcon_bmove(conp, t, 0, t+count, 0, b-t-count, 1425 conp->vc_cols); 1426 fbcon_clear(conp, t, 0, count, conp->vc_cols); 1427 break; 1428 1429 case __SCROLL_YREDRAW: 1430 redraw_down: 1431 fbcon_redraw(conp, p, b - 1, b-t-count, -count*conp->vc_cols); 1432 p->dispsw->clear(conp, p, real_y(p, t), 0, 1433 count, conp->vc_cols); 1434 scr_memsetw((unsigned short *)(conp->vc_origin + 1435 conp->vc_size_row * t), 1436 conp->vc_video_erase_char, 1437 conp->vc_size_row * count); 1438 return 1; 1439 } 1440 } 1441 return 0; 1442} 1443 1444 1445static void fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, 1446 int height, int width) 1447{ 1448 int unit = conp->vc_num; 1449 struct display *p = &fb_display[unit]; 1450 1451 if (!p->can_soft_blank && console_blanked) 1452 return; 1453 1454 if (!width || !height) 1455 return; 1456 1457 if (((sy <= p->cursor_y) && (p->cursor_y < sy+height) && 1458 (sx <= p->cursor_x) && (p->cursor_x < sx+width)) || 1459 ((dy <= p->cursor_y) && (p->cursor_y < dy+height) && 1460 (dx <= p->cursor_x) && (p->cursor_x < dx+width))) 1461 fbcon_cursor(conp, CM_ERASE|CM_SOFTBACK); 1462 1463 /* Split blits that cross physical y_wrap case. 1464 * Pathological case involves 4 blits, better to use recursive 1465 * code rather than unrolled case 1466 * 1467 * Recursive invocations don't need to erase the cursor over and 1468 * over again, so we use fbcon_bmove_rec() 1469 */ 1470 fbcon_bmove_rec(p, sy, sx, dy, dx, height, width, p->vrows-p->yscroll); 1471} 1472 1473static void fbcon_bmove_rec(struct display *p, int sy, int sx, int dy, int dx, 1474 int height, int width, u_int y_break) 1475{ 1476 u_int b; 1477 1478 if (sy < y_break && sy+height > y_break) { 1479 b = y_break-sy; 1480 if (dy < sy) { /* Avoid trashing self */ 1481 fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break); 1482 fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break); 1483 } else { 1484 fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break); 1485 fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break); 1486 } 1487 return; 1488 } 1489 1490 if (dy < y_break && dy+height > y_break) { 1491 b = y_break-dy; 1492 if (dy < sy) { /* Avoid trashing self */ 1493 fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break); 1494 fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break); 1495 } else { 1496 fbcon_bmove_rec(p, sy+b, sx, dy+b, dx, height-b, width, y_break); 1497 fbcon_bmove_rec(p, sy, sx, dy, dx, b, width, y_break); 1498 } 1499 return; 1500 } 1501 p->dispsw->bmove(p, real_y(p, sy), sx, real_y(p, dy), dx, height, width); 1502} 1503 1504 1505static int fbcon_switch(struct vc_data *conp) 1506{ 1507 int unit = conp->vc_num; 1508 struct display *p = &fb_display[unit]; 1509 struct fb_info *info = p->fb_info; 1510 1511 if (softback_top) { 1512 int l = fbcon_softback_size / conp->vc_size_row; 1513 if (softback_lines) 1514 fbcon_set_origin(conp); 1515 softback_top = softback_curr = softback_in = softback_buf; 1516 softback_lines = 0; 1517 1518 if (l > 5) 1519 softback_end = softback_buf + l * conp->vc_size_row; 1520 else { 1521 /* Smaller scrollback makes no sense, and 0 would screw 1522 the operation totally */ 1523 softback_top = 0; 1524 } 1525 } 1526 if (logo_shown >= 0) { 1527 struct vc_data *conp2 = vc_cons[logo_shown].d; 1528 1529 if (conp2->vc_top == logo_lines && conp2->vc_bottom == conp2->vc_rows) 1530 conp2->vc_top = 0; 1531 logo_shown = -1; 1532 } 1533 p->var.yoffset = p->yscroll = 0; 1534 switch (p->scrollmode & __SCROLL_YMASK) { 1535 case __SCROLL_YWRAP: 1536 scrollback_phys_max = p->vrows-conp->vc_rows; 1537 break; 1538 case __SCROLL_YPAN: 1539 scrollback_phys_max = p->vrows-2*conp->vc_rows; 1540 if (scrollback_phys_max < 0) 1541 scrollback_phys_max = 0; 1542 break; 1543 default: 1544 scrollback_phys_max = 0; 1545 break; 1546 } 1547 scrollback_max = 0; 1548 scrollback_current = 0; 1549 1550 if (info && info->switch_con) 1551 (*info->switch_con)(unit, info); 1552 if (p->dispsw->clear_margins && vt_cons[unit]->vc_mode == KD_TEXT) 1553 p->dispsw->clear_margins(conp, p, 0); 1554 if (logo_shown == -2) { 1555 logo_shown = fg_console; 1556 fbcon_show_logo(); /* This is protected above by initmem_freed */ 1557 update_region(fg_console, 1558 conp->vc_origin + conp->vc_size_row * conp->vc_top, 1559 conp->vc_size_row * (conp->vc_bottom - conp->vc_top) / 2); 1560 return 0; 1561 } 1562 return 1; 1563} 1564 1565 1566static int fbcon_blank(struct vc_data *conp, int blank) 1567{ 1568 struct display *p = &fb_display[conp->vc_num]; 1569 struct fb_info *info = p->fb_info; 1570 1571 if (blank < 0) /* Entering graphics mode */ 1572 return 0; 1573 1574 fbcon_cursor(p->conp, blank ? CM_ERASE : CM_DRAW); 1575 1576 if (!p->can_soft_blank) { 1577 if (blank) { 1578 if (p->visual == FB_VISUAL_MONO01) { 1579 if (p->screen_base) 1580 fb_memset255(p->screen_base, 1581 p->var.xres_virtual*p->var.yres_virtual* 1582 p->var.bits_per_pixel>>3); 1583 } else { 1584 unsigned short oldc; 1585 u_int height; 1586 u_int y_break; 1587 1588 oldc = conp->vc_video_erase_char; 1589 conp->vc_video_erase_char &= p->charmask; 1590 height = conp->vc_rows; 1591 y_break = p->vrows-p->yscroll; 1592 if (height > y_break) { 1593 p->dispsw->clear(conp, p, real_y(p, 0), 0, y_break, conp->vc_cols); 1594 p->dispsw->clear(conp, p, real_y(p, y_break), 0, height-y_break, conp->vc_cols); 1595 } else 1596 p->dispsw->clear(conp, p, real_y(p, 0), 0, height, conp->vc_cols); 1597 conp->vc_video_erase_char = oldc; 1598 } 1599 return 0; 1600 } else { 1601 /* Tell console.c that it has to restore the screen itself */ 1602 return 1; 1603 } 1604 } 1605 (*info->blank)(blank, info); 1606 return 0; 1607} 1608 1609static void fbcon_free_font(struct display *p) 1610{ 1611 if (p->userfont && p->fontdata && 1612 (--REFCOUNT(p->fontdata) == 0)) 1613 kfree(p->fontdata - FONT_EXTRA_WORDS*sizeof(int)); 1614 p->fontdata = NULL; 1615 p->userfont = 0; 1616} 1617 1618static inline int fbcon_get_font(int unit, struct console_font_op *op) 1619{ 1620 struct display *p = &fb_display[unit]; 1621 u8 *data = op->data; 1622 u8 *fontdata = p->fontdata; 1623 int i, j; 1624 1625#ifdef CONFIG_FBCON_FONTWIDTH8_ONLY 1626 if (fontwidth(p) != 8) return -EINVAL; 1627#endif 1628 op->width = fontwidth(p); 1629 op->height = fontheight(p); 1630 op->charcount = (p->charmask == 0x1ff) ? 512 : 256; 1631 if (!op->data) return 0; 1632 1633 if (op->width <= 8) { 1634 j = fontheight(p); 1635 for (i = 0; i < op->charcount; i++) { 1636 memcpy(data, fontdata, j); 1637 memset(data+j, 0, 32-j); 1638 data += 32; 1639 fontdata += j; 1640 } 1641 } 1642#ifndef CONFIG_FBCON_FONTWIDTH8_ONLY 1643 else if (op->width <= 16) { 1644 j = fontheight(p) * 2; 1645 for (i = 0; i < op->charcount; i++) { 1646 memcpy(data, fontdata, j); 1647 memset(data+j, 0, 64-j); 1648 data += 64; 1649 fontdata += j; 1650 } 1651 } else if (op->width <= 24) { 1652 for (i = 0; i < op->charcount; i++) { 1653 for (j = 0; j < fontheight(p); j++) { 1654 *data++ = fontdata[0]; 1655 *data++ = fontdata[1]; 1656 *data++ = fontdata[2]; 1657 fontdata += sizeof(u32); 1658 } 1659 memset(data, 0, 3*(32-j)); 1660 data += 3 * (32 - j); 1661 } 1662 } else { 1663 j = fontheight(p) * 4; 1664 for (i = 0; i < op->charcount; i++) { 1665 memcpy(data, fontdata, j); 1666 memset(data+j, 0, 128-j); 1667 data += 128; 1668 fontdata += j; 1669 } 1670 } 1671#endif 1672 return 0; 1673} 1674 1675static int fbcon_do_set_font(int unit, struct console_font_op *op, u8 *data, int userfont) 1676{ 1677 struct display *p = &fb_display[unit]; 1678 int resize; 1679 int w = op->width; 1680 int h = op->height; 1681 int cnt; 1682 char *old_data = NULL; 1683 1684 if (!fontwidthvalid(p,w)) { 1685 if (userfont && op->op != KD_FONT_OP_COPY) 1686 kfree(data - FONT_EXTRA_WORDS*sizeof(int)); 1687 return -ENXIO; 1688 } 1689 1690 if (CON_IS_VISIBLE(p->conp) && softback_lines) 1691 fbcon_set_origin(p->conp); 1692 1693 resize = (w != fontwidth(p)) || (h != fontheight(p)); 1694 if (p->userfont) 1695 old_data = p->fontdata; 1696 if (userfont) 1697 cnt = FNTCHARCNT(data); 1698 else 1699 cnt = 256; 1700 p->fontdata = data; 1701 if ((p->userfont = userfont)) 1702 REFCOUNT(data)++; 1703 p->_fontwidth = w; 1704 p->_fontheight = h; 1705 if (p->conp->vc_hi_font_mask && cnt == 256) { 1706 p->conp->vc_hi_font_mask = 0; 1707 if (p->conp->vc_can_do_color) 1708 p->conp->vc_complement_mask >>= 1; 1709 p->fgshift--; 1710 p->bgshift--; 1711 p->charmask = 0xff; 1712 1713 /* ++Edmund: reorder the attribute bits */ 1714 if (p->conp->vc_can_do_color) { 1715 struct vc_data *conp = p->conp; 1716 unsigned short *cp = (unsigned short *) conp->vc_origin; 1717 int count = conp->vc_screenbuf_size/2; 1718 unsigned short c; 1719 for (; count > 0; count--, cp++) { 1720 c = scr_readw(cp); 1721 scr_writew(((c & 0xfe00) >> 1) | (c & 0xff), cp); 1722 } 1723 c = conp->vc_video_erase_char; 1724 conp->vc_video_erase_char = ((c & 0xfe00) >> 1) | (c & 0xff); 1725 conp->vc_attr >>= 1; 1726 } 1727 1728 } else if (!p->conp->vc_hi_font_mask && cnt == 512) { 1729 p->conp->vc_hi_font_mask = 0x100; 1730 if (p->conp->vc_can_do_color) 1731 p->conp->vc_complement_mask <<= 1; 1732 p->fgshift++; 1733 p->bgshift++; 1734 p->charmask = 0x1ff; 1735 1736 /* ++Edmund: reorder the attribute bits */ 1737 { 1738 struct vc_data *conp = p->conp; 1739 unsigned short *cp = (unsigned short *) conp->vc_origin; 1740 int count = conp->vc_screenbuf_size/2; 1741 unsigned short c; 1742 for (; count > 0; count--, cp++) { 1743 unsigned short newc; 1744 c = scr_readw(cp); 1745 if (conp->vc_can_do_color) 1746 newc = ((c & 0xff00) << 1) | (c & 0xff); 1747 else 1748 newc = c & ~0x100; 1749 scr_writew(newc, cp); 1750 } 1751 c = conp->vc_video_erase_char; 1752 if (conp->vc_can_do_color) { 1753 conp->vc_video_erase_char = ((c & 0xff00) << 1) | (c & 0xff); 1754 conp->vc_attr <<= 1; 1755 } else 1756 conp->vc_video_erase_char = c & ~0x100; 1757 } 1758 1759 } 1760 fbcon_font_widths(p); 1761 1762 if (resize) { 1763 struct vc_data *conp = p->conp; 1764 /* reset wrap/pan */ 1765 p->var.xoffset = p->var.yoffset = p->yscroll = 0; 1766 p->vrows = p->var.yres_virtual/h; 1767 if ((p->var.yres % h) && (p->var.yres_virtual % h < p->var.yres % h)) 1768 p->vrows--; 1769 updatescrollmode(p); 1770 vc_resize_con( p->var.yres/h, p->var.xres/w, unit ); 1771 if (CON_IS_VISIBLE(conp) && softback_buf) { 1772 int l = fbcon_softback_size / conp->vc_size_row; 1773 if (l > 5) 1774 softback_end = softback_buf + l * conp->vc_size_row; 1775 else { 1776 /* Smaller scrollback makes no sense, and 0 would screw 1777 the operation totally */ 1778 softback_top = 0; 1779 } 1780 } 1781 } else if (CON_IS_VISIBLE(p->conp) && vt_cons[unit]->vc_mode == KD_TEXT) { 1782 if (p->dispsw->clear_margins) 1783 p->dispsw->clear_margins(p->conp, p, 0); 1784 update_screen(unit); 1785 } 1786 1787 if (old_data && (--REFCOUNT(old_data) == 0)) 1788 kfree(old_data - FONT_EXTRA_WORDS*sizeof(int)); 1789 1790 return 0; 1791} 1792 1793static inline int fbcon_copy_font(int unit, struct console_font_op *op) 1794{ 1795 struct display *od, *p = &fb_display[unit]; 1796 int h = op->height; 1797 1798 if (h < 0 || !vc_cons_allocated( h )) 1799 return -ENOTTY; 1800 if (h == unit) 1801 return 0; /* nothing to do */ 1802 od = &fb_display[h]; 1803 if (od->fontdata == p->fontdata) 1804 return 0; /* already the same font... */ 1805 op->width = fontwidth(od); 1806 op->height = fontheight(od); 1807 return fbcon_do_set_font(unit, op, od->fontdata, od->userfont); 1808} 1809 1810static inline int fbcon_set_font(int unit, struct console_font_op *op) 1811{ 1812 int w = op->width; 1813 int h = op->height; 1814 int size = h; 1815 int i, k; 1816 u8 *new_data, *data = op->data, *p; 1817 1818#ifdef CONFIG_FBCON_FONTWIDTH8_ONLY 1819 if (w != 8) 1820 return -EINVAL; 1821#endif 1822 if ((w <= 0) || (w > 32) || (op->charcount != 256 && op->charcount != 512)) 1823 return -EINVAL; 1824 1825 if (w > 8) { 1826 if (w <= 16) 1827 size *= 2; 1828 else 1829 size *= 4; 1830 } 1831 size *= op->charcount; 1832 1833 if (!(new_data = kmalloc(FONT_EXTRA_WORDS*sizeof(int)+size, GFP_USER))) 1834 return -ENOMEM; 1835 new_data += FONT_EXTRA_WORDS*sizeof(int); 1836 FNTSIZE(new_data) = size; 1837 FNTCHARCNT(new_data) = op->charcount; 1838 REFCOUNT(new_data) = 0; /* usage counter */ 1839 p = new_data; 1840 if (w <= 8) { 1841 for (i = 0; i < op->charcount; i++) { 1842 memcpy(p, data, h); 1843 data += 32; 1844 p += h; 1845 } 1846 } 1847#ifndef CONFIG_FBCON_FONTWIDTH8_ONLY 1848 else if (w <= 16) { 1849 h *= 2; 1850 for (i = 0; i < op->charcount; i++) { 1851 memcpy(p, data, h); 1852 data += 64; 1853 p += h; 1854 } 1855 } else if (w <= 24) { 1856 for (i = 0; i < op->charcount; i++) { 1857 int j; 1858 for (j = 0; j < h; j++) { 1859 memcpy(p, data, 3); 1860 p[3] = 0; 1861 data += 3; 1862 p += sizeof(u32); 1863 } 1864 data += 3*(32 - h); 1865 } 1866 } else { 1867 h *= 4; 1868 for (i = 0; i < op->charcount; i++) { 1869 memcpy(p, data, h); 1870 data += 128; 1871 p += h; 1872 } 1873 } 1874#endif 1875 /* we can do it in u32 chunks because of charcount is 256 or 512, so 1876 font length must be multiple of 256, at least. And 256 is multiple 1877 of 4 */ 1878 k = 0; 1879 while (p > new_data) k += *--(u32 *)p; 1880 FNTSUM(new_data) = k; 1881 /* Check if the same font is on some other console already */ 1882 for (i = 0; i < MAX_NR_CONSOLES; i++) { 1883 if (fb_display[i].userfont && 1884 fb_display[i].fontdata && 1885 FNTSUM(fb_display[i].fontdata) == k && 1886 FNTSIZE(fb_display[i].fontdata) == size && 1887 fontwidth(&fb_display[i]) == w && 1888 !memcmp(fb_display[i].fontdata, new_data, size)) { 1889 kfree(new_data - FONT_EXTRA_WORDS*sizeof(int)); 1890 new_data = fb_display[i].fontdata; 1891 break; 1892 } 1893 } 1894 return fbcon_do_set_font(unit, op, new_data, 1); 1895} 1896 1897static inline int fbcon_set_def_font(int unit, struct console_font_op *op) 1898{ 1899 char name[MAX_FONT_NAME]; 1900 struct fbcon_font_desc *f; 1901 struct display *p = &fb_display[unit]; 1902 1903 if (!op->data) 1904 f = fbcon_get_default_font(p->var.xres, p->var.yres); 1905 else if (strncpy_from_user(name, op->data, MAX_FONT_NAME-1) < 0) 1906 return -EFAULT; 1907 else { 1908 name[MAX_FONT_NAME-1] = 0; 1909 if (!(f = fbcon_find_font(name))) 1910 return -ENOENT; 1911 } 1912 op->width = f->width; 1913 op->height = f->height; 1914 return fbcon_do_set_font(unit, op, f->data, 0); 1915} 1916 1917static int fbcon_font_op(struct vc_data *conp, struct console_font_op *op) 1918{ 1919 int unit = conp->vc_num; 1920 1921 switch (op->op) { 1922 case KD_FONT_OP_SET: 1923 return fbcon_set_font(unit, op); 1924 case KD_FONT_OP_GET: 1925 return fbcon_get_font(unit, op); 1926 case KD_FONT_OP_SET_DEFAULT: 1927 return fbcon_set_def_font(unit, op); 1928 case KD_FONT_OP_COPY: 1929 return fbcon_copy_font(unit, op); 1930 default: 1931 return -ENOSYS; 1932 } 1933} 1934 1935static u16 palette_red[16]; 1936static u16 palette_green[16]; 1937static u16 palette_blue[16]; 1938 1939static struct fb_cmap palette_cmap = { 1940 0, 16, palette_red, palette_green, palette_blue, NULL 1941}; 1942 1943static int fbcon_set_palette(struct vc_data *conp, unsigned char *table) 1944{ 1945 int unit = conp->vc_num; 1946 struct display *p = &fb_display[unit]; 1947 int i, j, k; 1948 u8 val; 1949 1950 if (!conp->vc_can_do_color || (!p->can_soft_blank && console_blanked)) 1951 return -EINVAL; 1952 for (i = j = 0; i < 16; i++) { 1953 k = table[i]; 1954 val = conp->vc_palette[j++]; 1955 palette_red[k] = (val<<8)|val; 1956 val = conp->vc_palette[j++]; 1957 palette_green[k] = (val<<8)|val; 1958 val = conp->vc_palette[j++]; 1959 palette_blue[k] = (val<<8)|val; 1960 } 1961 if (p->var.bits_per_pixel <= 4) 1962 palette_cmap.len = 1<<p->var.bits_per_pixel; 1963 else 1964 palette_cmap.len = 16; 1965 palette_cmap.start = 0; 1966 return p->fb_info->fbops->fb_set_cmap(&palette_cmap, 1, unit, p->fb_info); 1967} 1968 1969static u16 *fbcon_screen_pos(struct vc_data *conp, int offset) 1970{ 1971 int line; 1972 unsigned long p; 1973 1974 if (conp->vc_num != fg_console || !softback_lines) 1975 return (u16 *)(conp->vc_origin + offset); 1976 line = offset / conp->vc_size_row; 1977 if (line >= softback_lines) 1978 return (u16 *)(conp->vc_origin + offset - softback_lines * conp->vc_size_row); 1979 p = softback_curr + offset; 1980 if (p >= softback_end) 1981 p += softback_buf - softback_end; 1982 return (u16 *)p; 1983} 1984 1985static unsigned long fbcon_getxy(struct vc_data *conp, unsigned long pos, int *px, int *py) 1986{ 1987 int x, y; 1988 unsigned long ret; 1989 if (pos >= conp->vc_origin && pos < conp->vc_scr_end) { 1990 unsigned long offset = (pos - conp->vc_origin) / 2; 1991 1992 x = offset % conp->vc_cols; 1993 y = offset / conp->vc_cols; 1994 if (conp->vc_num == fg_console) 1995 y += softback_lines; 1996 ret = pos + (conp->vc_cols - x) * 2; 1997 } else if (conp->vc_num == fg_console && softback_lines) { 1998 unsigned long offset = pos - softback_curr; 1999 2000 if (pos < softback_curr) 2001 offset += softback_end - softback_buf; 2002 offset /= 2; 2003 x = offset % conp->vc_cols; 2004 y = offset / conp->vc_cols; 2005 ret = pos + (conp->vc_cols - x) * 2; 2006 if (ret == softback_end) 2007 ret = softback_buf; 2008 if (ret == softback_in) 2009 ret = conp->vc_origin; 2010 } else { 2011 /* Should not happen */ 2012 x = y = 0; 2013 ret = conp->vc_origin; 2014 } 2015 if (px) *px = x; 2016 if (py) *py = y; 2017 return ret; 2018} 2019 2020/* As we might be inside of softback, we may work with non-contiguous buffer, 2021 that's why we have to use a separate routine. */ 2022static void fbcon_invert_region(struct vc_data *conp, u16 *p, int cnt) 2023{ 2024 while (cnt--) { 2025 u16 a = scr_readw(p); 2026 if (!conp->vc_can_do_color) 2027 a ^= 0x0800; 2028 else if (conp->vc_hi_font_mask == 0x100) 2029 a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) | (((a) & 0x0e00) << 4); 2030 else 2031 a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4); 2032 scr_writew(a, p++); 2033 if (p == (u16 *)softback_end) 2034 p = (u16 *)softback_buf; 2035 if (p == (u16 *)softback_in) 2036 p = (u16 *)conp->vc_origin; 2037 } 2038} 2039 2040static int fbcon_scrolldelta(struct vc_data *conp, int lines) 2041{ 2042 int unit, offset, limit, scrollback_old; 2043 struct display *p; 2044 2045 unit = fg_console; 2046 p = &fb_display[unit]; 2047 if (softback_top) { 2048 if (conp->vc_num != unit) 2049 return 0; 2050 if (vt_cons[unit]->vc_mode != KD_TEXT || !lines) 2051 return 0; 2052 if (logo_shown >= 0) { 2053 struct vc_data *conp2 = vc_cons[logo_shown].d; 2054 2055 if (conp2->vc_top == logo_lines && conp2->vc_bottom == conp2->vc_rows) 2056 conp2->vc_top = 0; 2057 if (logo_shown == unit) { 2058 unsigned long p, q; 2059 int i; 2060 2061 p = softback_in; 2062 q = conp->vc_origin + logo_lines * conp->vc_size_row; 2063 for (i = 0; i < logo_lines; i++) { 2064 if (p == softback_top) break; 2065 if (p == softback_buf) p = softback_end; 2066 p -= conp->vc_size_row; 2067 q -= conp->vc_size_row; 2068 scr_memcpyw((u16 *)q, (u16 *)p, conp->vc_size_row); 2069 } 2070 softback_in = p; 2071 update_region(unit, conp->vc_origin, logo_lines * conp->vc_cols); 2072 } 2073 logo_shown = -1; 2074 } 2075 fbcon_cursor(conp, CM_ERASE|CM_SOFTBACK); 2076 fbcon_redraw_softback(conp, p, lines); 2077 fbcon_cursor(conp, CM_DRAW|CM_SOFTBACK); 2078 return 0; 2079 } 2080 2081 if (!scrollback_phys_max) 2082 return -ENOSYS; 2083 2084 scrollback_old = scrollback_current; 2085 scrollback_current -= lines; 2086 if (scrollback_current < 0) 2087 scrollback_current = 0; 2088 else if (scrollback_current > scrollback_max) 2089 scrollback_current = scrollback_max; 2090 if (scrollback_current == scrollback_old) 2091 return 0; 2092 2093 if (!p->can_soft_blank && 2094 (console_blanked || vt_cons[unit]->vc_mode != KD_TEXT || !lines)) 2095 return 0; 2096 fbcon_cursor(conp, CM_ERASE); 2097 2098 offset = p->yscroll-scrollback_current; 2099 limit = p->vrows; 2100 switch (p->scrollmode && __SCROLL_YMASK) { 2101 case __SCROLL_YWRAP: 2102 p->var.vmode |= FB_VMODE_YWRAP; 2103 break; 2104 case __SCROLL_YPAN: 2105 limit -= conp->vc_rows; 2106 p->var.vmode &= ~FB_VMODE_YWRAP; 2107 break; 2108 } 2109 if (offset < 0) 2110 offset += limit; 2111 else if (offset >= limit) 2112 offset -= limit; 2113 p->var.xoffset = 0; 2114 p->var.yoffset = offset*fontheight(p); 2115 p->fb_info->updatevar(unit, p->fb_info); 2116 if (!scrollback_current) 2117 fbcon_cursor(conp, CM_DRAW); 2118 return 0; 2119} 2120 2121static int fbcon_set_origin(struct vc_data *conp) 2122{ 2123 if (softback_lines && !console_blanked) 2124 fbcon_scrolldelta(conp, softback_lines); 2125 return 0; 2126} 2127 2128static inline unsigned safe_shift(unsigned d,int n) 2129{ 2130 return n<0 ? d>>-n : d<<n; 2131} 2132 2133static int __init fbcon_show_logo( void ) 2134{ 2135 struct display *p = &fb_display[fg_console]; /* draw to vt in foreground */ 2136 int depth = p->var.bits_per_pixel; 2137 int line = p->next_line; 2138 unsigned char *fb = p->screen_base; 2139 unsigned char *logo; 2140 unsigned char *dst, *src; 2141 int i, j, n, x1, y1, x; 2142 int logo_depth, done = 0; 2143 2144 /* Return if the frame buffer is not mapped */ 2145 if (!fb) 2146 return 0; 2147 2148 /* 2149 * Set colors if visual is PSEUDOCOLOR and we have enough colors, or for 2150 * DIRECTCOLOR 2151 * We don't have to set the colors for the 16-color logo, since that logo 2152 * uses the standard VGA text console palette 2153 */ 2154 if ((p->visual == FB_VISUAL_PSEUDOCOLOR && depth >= 8) || 2155 (p->visual == FB_VISUAL_DIRECTCOLOR && depth >= 24)) 2156 for (i = 0; i < LINUX_LOGO_COLORS; i += n) { 2157 n = LINUX_LOGO_COLORS - i; 2158 if (n > 16) 2159 /* palette_cmap provides space for only 16 colors at once */ 2160 n = 16; 2161 palette_cmap.start = 32 + i; 2162 palette_cmap.len = n; 2163 for( j = 0; j < n; ++j ) { 2164 palette_cmap.red[j] = (linux_logo_red[i+j] << 8) | 2165 linux_logo_red[i+j]; 2166 palette_cmap.green[j] = (linux_logo_green[i+j] << 8) | 2167 linux_logo_green[i+j]; 2168 palette_cmap.blue[j] = (linux_logo_blue[i+j] << 8) | 2169 linux_logo_blue[i+j]; 2170 } 2171 p->fb_info->fbops->fb_set_cmap(&palette_cmap, 1, fg_console, 2172 p->fb_info); 2173 } 2174 2175 if (depth >= 8) { 2176 logo = linux_logo; 2177 logo_depth = 8; 2178 } 2179 else if (depth >= 4) { 2180 logo = linux_logo16; 2181 logo_depth = 4; 2182 } 2183 else { 2184 logo = linux_logo_bw; 2185 logo_depth = 1; 2186 } 2187 2188 if (p->fb_info->fbops->fb_rasterimg) 2189 p->fb_info->fbops->fb_rasterimg(p->fb_info, 1); 2190 2191 for (x = 0; x < smp_num_cpus * (LOGO_W + 8) && 2192 x < p->var.xres - (LOGO_W + 8); x += (LOGO_W + 8)) { 2193 2194#if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CFB24) || \ 2195 defined(CONFIG_FBCON_CFB32) || defined(CONFIG_FB_SBUS) 2196 if (p->visual == FB_VISUAL_DIRECTCOLOR) { 2197 unsigned int val; /* max. depth 32! */ 2198 int bdepth; 2199 int redshift, greenshift, blueshift; 2200 2201 /* Bug: Doesn't obey msb_right ... (who needs that?) */ 2202 redshift = p->var.red.offset; 2203 greenshift = p->var.green.offset; 2204 blueshift = p->var.blue.offset; 2205 2206 if (depth >= 24 && (depth % 8) == 0) { 2207 /* have at least 8 bits per color */ 2208 src = logo; 2209 bdepth = depth/8; 2210 for( y1 = 0; y1 < LOGO_H; y1++ ) { 2211 dst = fb + y1*line + x*bdepth; 2212 for( x1 = 0; x1 < LOGO_W; x1++, src++ ) { 2213 val = (*src << redshift) | 2214 (*src << greenshift) | 2215 (*src << blueshift); 2216 if (bdepth == 4 && !((long)dst & 3)) { 2217 /* Some cards require 32bit access */ 2218 fb_writel (val, dst); 2219 dst += 4; 2220 } else if (bdepth == 2 && !((long)dst & 1)) { 2221 /* others require 16bit access */ 2222 fb_writew (val,dst); 2223 dst +=2; 2224 } else { 2225#ifdef __LITTLE_ENDIAN 2226 for( i = 0; i < bdepth; ++i ) 2227#else 2228 for( i = bdepth-1; i >= 0; --i ) 2229#endif 2230 fb_writeb (val >> (i*8), dst++); 2231 } 2232 } 2233 } 2234 } 2235 else if (depth >= 12 && depth <= 23) { 2236 /* have 4..7 bits per color, using 16 color image */ 2237 unsigned int pix; 2238 src = linux_logo16; 2239 bdepth = (depth+7)/8; 2240 for( y1 = 0; y1 < LOGO_H; y1++ ) { 2241 dst = fb + y1*line + x*bdepth; 2242 for( x1 = 0; x1 < LOGO_W/2; x1++, src++ ) { 2243 pix = *src >> 4; /* upper nibble */ 2244 val = (pix << redshift) | 2245 (pix << greenshift) | 2246 (pix << blueshift); 2247#ifdef __LITTLE_ENDIAN 2248 for( i = 0; i < bdepth; ++i ) 2249#else 2250 for( i = bdepth-1; i >= 0; --i ) 2251#endif 2252 fb_writeb (val >> (i*8), dst++); 2253 pix = *src & 0x0f; /* lower nibble */ 2254 val = (pix << redshift) | 2255 (pix << greenshift) | 2256 (pix << blueshift); 2257#ifdef __LITTLE_ENDIAN 2258 for( i = 0; i < bdepth; ++i ) 2259#else 2260 for( i = bdepth-1; i >= 0; --i ) 2261#endif 2262 fb_writeb (val >> (i*8), dst++); 2263 } 2264 } 2265 } 2266 done = 1; 2267 } 2268#endif 2269#if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CFB24) || \ 2270 defined(CONFIG_FBCON_CFB32) || defined(CONFIG_FB_SBUS) 2271 if ((depth % 8 == 0) && (p->visual == FB_VISUAL_TRUECOLOR)) { 2272 /* Modes without color mapping, needs special data transformation... */ 2273 unsigned int val; /* max. depth 32! */ 2274 int bdepth = depth/8; 2275 unsigned char mask[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff }; 2276 unsigned char redmask, greenmask, bluemask; 2277 int redshift, greenshift, blueshift; 2278 2279 /* Bug: Doesn't obey msb_right ... (who needs that?) */ 2280 redmask = mask[p->var.red.length < 8 ? p->var.red.length : 8]; 2281 greenmask = mask[p->var.green.length < 8 ? p->var.green.length : 8]; 2282 bluemask = mask[p->var.blue.length < 8 ? p->var.blue.length : 8]; 2283 redshift = p->var.red.offset - (8-p->var.red.length); 2284 greenshift = p->var.green.offset - (8-p->var.green.length); 2285 blueshift = p->var.blue.offset - (8-p->var.blue.length); 2286 2287 src = logo; 2288 for( y1 = 0; y1 < LOGO_H; y1++ ) { 2289 dst = fb + y1*line + x*bdepth; 2290 for( x1 = 0; x1 < LOGO_W; x1++, src++ ) { 2291 val = safe_shift((linux_logo_red[*src-32] & redmask), redshift) | 2292 safe_shift((linux_logo_green[*src-32] & greenmask), greenshift) | 2293 safe_shift((linux_logo_blue[*src-32] & bluemask), blueshift); 2294 if (bdepth == 4 && !((long)dst & 3)) { 2295 /* Some cards require 32bit access */ 2296 fb_writel (val, dst); 2297 dst += 4; 2298 } else if (bdepth == 2 && !((long)dst & 1)) { 2299 /* others require 16bit access */ 2300 fb_writew (val,dst); 2301 dst +=2; 2302 } else { 2303#ifdef __LITTLE_ENDIAN 2304 for( i = 0; i < bdepth; ++i ) 2305#else 2306 for( i = bdepth-1; i >= 0; --i ) 2307#endif 2308 fb_writeb (val >> (i*8), dst++); 2309 } 2310 } 2311 } 2312 done = 1; 2313 } 2314#endif 2315#if defined(CONFIG_FBCON_CFB4) 2316 if (depth == 4 && p->type == FB_TYPE_PACKED_PIXELS) { 2317 src = logo; 2318 for( y1 = 0; y1 < LOGO_H; y1++) { 2319 dst = fb + y1*line + x/2; 2320 for( x1 = 0; x1 < LOGO_W/2; x1++) { 2321 u8 q = *src++; 2322 q = (q << 4) | (q >> 4); 2323 fb_writeb (q, dst++); 2324 } 2325 } 2326 done = 1; 2327 } 2328#endif 2329#if defined(CONFIG_FBCON_CFB8) || defined(CONFIG_FB_SBUS) 2330 if (depth == 8 && p->type == FB_TYPE_PACKED_PIXELS) { 2331 /* depth 8 or more, packed, with color registers */ 2332 2333 src = logo; 2334 for( y1 = 0; y1 < LOGO_H; y1++ ) { 2335 dst = fb + y1*line + x; 2336 for( x1 = 0; x1 < LOGO_W; x1++ ) 2337 fb_writeb (*src++, dst++); 2338 } 2339 done = 1; 2340 } 2341#endif 2342#if defined(CONFIG_FBCON_AFB) || defined(CONFIG_FBCON_ILBM) || \ 2343 defined(CONFIG_FBCON_IPLAN2P2) || defined(CONFIG_FBCON_IPLAN2P4) || \ 2344 defined(CONFIG_FBCON_IPLAN2P8) 2345 if (depth >= 2 && (p->type == FB_TYPE_PLANES || 2346 p->type == FB_TYPE_INTERLEAVED_PLANES)) { 2347 /* planes (normal or interleaved), with color registers */ 2348 int bit; 2349 unsigned char val, mask; 2350 int plane = p->next_plane; 2351 2352#if defined(CONFIG_FBCON_IPLAN2P2) || defined(CONFIG_FBCON_IPLAN2P4) || \ 2353 defined(CONFIG_FBCON_IPLAN2P8) 2354 int line_length = p->line_length; 2355 2356 /* for support of Atari interleaved planes */ 2357#define MAP_X(x) (line_length ? (x) : ((x) & ~1)*depth + ((x) & 1)) 2358#else 2359#define MAP_X(x) (x) 2360#endif 2361 /* extract a bit from the source image */ 2362#define BIT(p,pix,bit) (p[pix*logo_depth/8] & \ 2363 (1 << ((8-((pix*logo_depth)&7)-logo_depth) + bit))) 2364 2365 src = logo; 2366 for( y1 = 0; y1 < LOGO_H; y1++ ) { 2367 for( x1 = 0; x1 < LOGO_LINE; x1++, src += logo_depth ) { 2368 dst = fb + y1*line + MAP_X(x/8+x1); 2369 for( bit = 0; bit < logo_depth; bit++ ) { 2370 val = 0; 2371 for( mask = 0x80, i = 0; i < 8; mask >>= 1, i++ ) { 2372 if (BIT( src, i, bit )) 2373 val |= mask; 2374 } 2375 *dst = val; 2376 dst += plane; 2377 } 2378 } 2379 } 2380 2381 /* fill remaining planes */ 2382 if (depth > logo_depth) { 2383 for( y1 = 0; y1 < LOGO_H; y1++ ) { 2384 for( x1 = 0; x1 < LOGO_LINE; x1++ ) { 2385 dst = fb + y1*line + MAP_X(x/8+x1) + logo_depth*plane; 2386 for( i = logo_depth; i < depth; i++, dst += plane ) 2387 *dst = 0x00; 2388 } 2389 } 2390 } 2391 done = 1; 2392 break; 2393 } 2394#endif 2395#if defined(CONFIG_FBCON_MFB) || defined(CONFIG_FBCON_AFB) || \ 2396 defined(CONFIG_FBCON_ILBM) || defined(CONFIG_FBCON_HGA) 2397 2398 if (depth == 1 && (p->type == FB_TYPE_PACKED_PIXELS || 2399 p->type == FB_TYPE_PLANES || 2400 p->type == FB_TYPE_INTERLEAVED_PLANES)) { 2401 2402 /* monochrome */ 2403 unsigned char inverse = p->inverse || p->visual == FB_VISUAL_MONO01 2404 ? 0x00 : 0xff; 2405 2406 int is_hga = !strncmp(p->fb_info->modename, "HGA", 3); 2407 /* can't use simply memcpy because need to apply inverse */ 2408 for( y1 = 0; y1 < LOGO_H; y1++ ) { 2409 src = logo + y1*LOGO_LINE; 2410 if (is_hga) 2411 dst = fb + (y1%4)*8192 + (y1>>2)*line + x/8; 2412 else 2413 dst = fb + y1*line + x/8; 2414 for( x1 = 0; x1 < LOGO_LINE; ++x1 ) 2415 fb_writeb(*src++ ^ inverse, dst++); 2416 } 2417 done = 1; 2418 } 2419#endif 2420#if defined(CONFIG_FBCON_VGA_PLANES) 2421 if (depth == 4 && p->type == FB_TYPE_VGA_PLANES) { 2422 outb_p(1,0x3ce); outb_p(0xf,0x3cf); 2423 outb_p(3,0x3ce); outb_p(0,0x3cf); 2424 outb_p(5,0x3ce); outb_p(0,0x3cf); 2425 2426 src = logo; 2427 for (y1 = 0; y1 < LOGO_H; y1++) { 2428 for (x1 = 0; x1 < LOGO_W / 2; x1++) { 2429 dst = fb + y1*line + x1/4 + x/8; 2430 2431 outb_p(0,0x3ce); 2432 outb_p(*src >> 4,0x3cf); 2433 outb_p(8,0x3ce); 2434 outb_p(1 << (7 - x1 % 4 * 2),0x3cf); 2435 fb_readb (dst); 2436 fb_writeb (0, dst); 2437 2438 outb_p(0,0x3ce); 2439 outb_p(*src & 0xf,0x3cf); 2440 outb_p(8,0x3ce); 2441 outb_p(1 << (7 - (1 + x1 % 4 * 2)),0x3cf); 2442 fb_readb (dst); 2443 fb_writeb (0, dst); 2444 2445 src++; 2446 } 2447 } 2448 done = 1; 2449 } 2450#endif 2451 } 2452 2453 if (p->fb_info->fbops->fb_rasterimg) 2454 p->fb_info->fbops->fb_rasterimg(p->fb_info, 0); 2455 2456 /* Modes not yet supported: packed pixels with depth != 8 (does such a 2457 * thing exist in reality?) */ 2458 2459 return done ? (LOGO_H + fontheight(p) - 1) / fontheight(p) : 0 ; 2460} 2461 2462#ifdef CONFIG_PM 2463/* console.c doesn't do enough here */ 2464static int 2465pm_fbcon_request(struct pm_dev *dev, pm_request_t rqst, void *data) 2466{ 2467 unsigned long flags; 2468 2469 switch (rqst) 2470 { 2471 case PM_RESUME: 2472 acquire_console_sem(); 2473 fbcon_sleeping = 0; 2474 if (use_timer_cursor) { 2475 cursor_timer.expires = jiffies+HZ/50; 2476 add_timer(&cursor_timer); 2477 } 2478 release_console_sem(); 2479 break; 2480 case PM_SUSPEND: 2481 acquire_console_sem(); 2482 save_flags(flags); 2483 cli(); 2484 if (use_timer_cursor) 2485 del_timer(&cursor_timer); 2486 fbcon_sleeping = 1; 2487 restore_flags(flags); 2488 release_console_sem(); 2489 break; 2490 } 2491 return 0; 2492} 2493#endif /* CONFIG_PM */ 2494 2495/* 2496 * The console `switch' structure for the frame buffer based console 2497 */ 2498 2499const struct consw fb_con = { 2500 con_startup: fbcon_startup, 2501 con_init: fbcon_init, 2502 con_deinit: fbcon_deinit, 2503 con_clear: fbcon_clear, 2504 con_putc: fbcon_putc, 2505 con_putcs: fbcon_putcs, 2506 con_cursor: fbcon_cursor, 2507 con_scroll: fbcon_scroll, 2508 con_bmove: fbcon_bmove, 2509 con_switch: fbcon_switch, 2510 con_blank: fbcon_blank, 2511 con_font_op: fbcon_font_op, 2512 con_set_palette: fbcon_set_palette, 2513 con_scrolldelta: fbcon_scrolldelta, 2514 con_set_origin: fbcon_set_origin, 2515 con_invert_region: fbcon_invert_region, 2516 con_screen_pos: fbcon_screen_pos, 2517 con_getxy: fbcon_getxy, 2518}; 2519 2520 2521/* 2522 * Dummy Low Level Operations 2523 */ 2524 2525static void fbcon_dummy_op(void) {} 2526 2527#define DUMMY (void *)fbcon_dummy_op 2528 2529struct display_switch fbcon_dummy = { 2530 setup: DUMMY, 2531 bmove: DUMMY, 2532 clear: DUMMY, 2533 putc: DUMMY, 2534 putcs: DUMMY, 2535 revc: DUMMY, 2536}; 2537 2538 2539/* 2540 * Visible symbols for modules 2541 */ 2542 2543EXPORT_SYMBOL(fb_display); 2544EXPORT_SYMBOL(fbcon_redraw_bmove); 2545EXPORT_SYMBOL(fbcon_redraw_clear); 2546EXPORT_SYMBOL(fbcon_dummy); 2547EXPORT_SYMBOL(fb_con); 2548 2549MODULE_LICENSE("GPL"); 2550