Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Revert "fbdev: Garbage collect fbdev scrolling acceleration, part 1 (from TODO list)"

This reverts commit b3ec8cdf457e5e63d396fe1346cc788cf7c1b578.

Revert the second (of 2) commits which disabled scrolling acceleration
in fbcon/fbdev. It introduced a regression for fbdev-supported graphic
cards because of the performance penalty by doing screen scrolling by
software instead of using the existing graphic card 2D hardware
acceleration.

Console scrolling acceleration was disabled by dropping code which
checked at runtime the driver hardware capabilities for the
BINFO_HWACCEL_COPYAREA or FBINFO_HWACCEL_FILLRECT flags and if set, it
enabled scrollmode SCROLL_MOVE which uses hardware acceleration to move
screen contents. After dropping those checks scrollmode was hard-wired
to SCROLL_REDRAW instead, which forces all graphic cards to redraw every
character at the new screen position when scrolling.

This change effectively disabled all hardware-based scrolling acceleration for
ALL drivers, because now all kind of 2D hardware acceleration (bitblt,
fillrect) in the drivers isn't used any longer.

The original commit message mentions that only 3 DRM drivers (nouveau, omapdrm
and gma500) used hardware acceleration in the past and thus code for checking
and using scrolling acceleration is obsolete.

This statement is NOT TRUE, because beside the DRM drivers there are around 35
other fbdev drivers which depend on fbdev/fbcon and still provide hardware
acceleration for fbdev/fbcon.

The original commit message also states that syzbot found lots of bugs in fbcon
and thus it's "often the solution to just delete code and remove features".
This is true, and the bugs - which actually affected all users of fbcon,
including DRM - were fixed, or code was dropped like e.g. the support for
software scrollback in vgacon (commit 973c096f6a85).

So to further analyze which bugs were found by syzbot, I've looked through all
patches in drivers/video which were tagged with syzbot or syzkaller back to
year 2005. The vast majority fixed the reported issues on a higher level, e.g.
when screen is to be resized, or when font size is to be changed. The few ones
which touched driver code fixed a real driver bug, e.g. by adding a check.

But NONE of those patches touched code of either the SCROLL_MOVE or the
SCROLL_REDRAW case.

That means, there was no real reason why SCROLL_MOVE had to be ripped-out and
just SCROLL_REDRAW had to be used instead. The only reason I can imagine so far
was that SCROLL_MOVE wasn't used by DRM and as such it was assumed that it
could go away. That argument completely missed the fact that SCROLL_MOVE is
still heavily used by fbdev (non-DRM) drivers.

Some people mention that using memcpy() instead of the hardware acceleration is
pretty much the same speed. But that's not true, at least not for older graphic
cards and machines where we see speed decreases by factor 10 and more and thus
this change leads to console responsiveness way worse than before.

That's why the original commit is to be reverted. By reverting we
reintroduce hardware-based scrolling acceleration and fix the
performance regression for fbdev drivers.

There isn't any impact on DRM when reverting those patches.

Signed-off-by: Helge Deller <deller@gmx.de>
Acked-by: Geert Uytterhoeven <geert@linux-m68k.org>
Acked-by: Sven Schnelle <svens@stackframe.org>
Cc: stable@vger.kernel.org # v5.16+
Signed-off-by: Helge Deller <deller@gmx.de>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20220202135531.92183-2-deller@gmx.de

authored by

Helge Deller and committed by
Daniel Vetter
1148836f 92c4cfae

+678 -51
+5 -8
Documentation/gpu/todo.rst
··· 303 303 Garbage collect fbdev scrolling acceleration 304 304 -------------------------------------------- 305 305 306 - Scroll acceleration has been disabled in fbcon. Now it works as the old 307 - SCROLL_REDRAW mode. A ton of code was removed in fbcon.c and the hook bmove was 308 - removed from fbcon_ops. 309 - Remaining tasks: 306 + Scroll acceleration is disabled in fbcon by hard-wiring p->scrollmode = 307 + SCROLL_REDRAW. There's a ton of code this will allow us to remove: 310 308 311 - - a bunch of the hooks in fbcon_ops could be removed or simplified by calling 309 + - lots of code in fbcon.c 310 + 311 + - a bunch of the hooks in fbcon_ops, maybe the remaining hooks could be called 312 312 directly instead of the function table (with a switch on p->rotate) 313 313 314 314 - fb_copyarea is unused after this, and can be deleted from all drivers 315 - 316 - - after that, fb_copyarea can be deleted from fb_ops in include/linux/fb.h as 317 - well as cfb_copyarea 318 315 319 316 Note that not all acceleration code can be deleted, since clearing and cursor 320 317 support is still accelerated, which might be good candidates for further
+16
drivers/video/fbdev/core/bitblit.c
··· 43 43 } 44 44 } 45 45 46 + static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy, 47 + int sx, int dy, int dx, int height, int width) 48 + { 49 + struct fb_copyarea area; 50 + 51 + area.sx = sx * vc->vc_font.width; 52 + area.sy = sy * vc->vc_font.height; 53 + area.dx = dx * vc->vc_font.width; 54 + area.dy = dy * vc->vc_font.height; 55 + area.height = height * vc->vc_font.height; 56 + area.width = width * vc->vc_font.width; 57 + 58 + info->fbops->fb_copyarea(info, &area); 59 + } 60 + 46 61 static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy, 47 62 int sx, int height, int width) 48 63 { ··· 393 378 394 379 void fbcon_set_bitops(struct fbcon_ops *ops) 395 380 { 381 + ops->bmove = bit_bmove; 396 382 ops->clear = bit_clear; 397 383 ops->putcs = bit_putcs; 398 384 ops->clear_margins = bit_clear_margins;
+489 -20
drivers/video/fbdev/core/fbcon.c
··· 173 173 int count, int ypos, int xpos); 174 174 static void fbcon_clear_margins(struct vc_data *vc, int bottom_only); 175 175 static void fbcon_cursor(struct vc_data *vc, int mode); 176 + static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, 177 + int height, int width); 176 178 static int fbcon_switch(struct vc_data *vc); 177 179 static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch); 178 180 static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table); ··· 182 180 /* 183 181 * Internal routines 184 182 */ 183 + static __inline__ void ywrap_up(struct vc_data *vc, int count); 184 + static __inline__ void ywrap_down(struct vc_data *vc, int count); 185 + static __inline__ void ypan_up(struct vc_data *vc, int count); 186 + static __inline__ void ypan_down(struct vc_data *vc, int count); 187 + static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx, 188 + int dy, int dx, int height, int width, u_int y_break); 185 189 static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, 186 190 int unit); 191 + static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p, 192 + int line, int count, int dy); 187 193 static void fbcon_modechanged(struct fb_info *info); 188 194 static void fbcon_set_all_vcs(struct fb_info *info); 189 195 static void fbcon_start(void); ··· 1136 1126 ops->graphics = 0; 1137 1127 1138 1128 /* 1129 + * No more hw acceleration for fbcon. 1130 + * 1131 + * FIXME: Garbage collect all the now dead code after sufficient time 1132 + * has passed. 1133 + */ 1134 + p->scrollmode = SCROLL_REDRAW; 1135 + 1136 + /* 1139 1137 * ++guenther: console.c:vc_allocate() relies on initializing 1140 1138 * vc_{cols,rows}, but we must not set those if we are only 1141 1139 * resizing the console. ··· 1229 1211 * This system is now divided into two levels because of complications 1230 1212 * caused by hardware scrolling. Top level functions: 1231 1213 * 1232 - * fbcon_clear(), fbcon_putc(), fbcon_clear_margins() 1214 + * fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins() 1233 1215 * 1234 1216 * handles y values in range [0, scr_height-1] that correspond to real 1235 1217 * screen positions. y_wrap shift means that first line of bitmap may be 1236 1218 * anywhere on this display. These functions convert lineoffsets to 1237 1219 * bitmap offsets and deal with the wrap-around case by splitting blits. 1238 1220 * 1221 + * fbcon_bmove_physical_8() -- These functions fast implementations 1239 1222 * fbcon_clear_physical_8() -- of original fbcon_XXX fns. 1240 1223 * fbcon_putc_physical_8() -- (font width != 8) may be added later 1241 1224 * ··· 1409 1390 } 1410 1391 } 1411 1392 1393 + static __inline__ void ywrap_up(struct vc_data *vc, int count) 1394 + { 1395 + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 1396 + struct fbcon_ops *ops = info->fbcon_par; 1397 + struct fbcon_display *p = &fb_display[vc->vc_num]; 1398 + 1399 + p->yscroll += count; 1400 + if (p->yscroll >= p->vrows) /* Deal with wrap */ 1401 + p->yscroll -= p->vrows; 1402 + ops->var.xoffset = 0; 1403 + ops->var.yoffset = p->yscroll * vc->vc_font.height; 1404 + ops->var.vmode |= FB_VMODE_YWRAP; 1405 + ops->update_start(info); 1406 + scrollback_max += count; 1407 + if (scrollback_max > scrollback_phys_max) 1408 + scrollback_max = scrollback_phys_max; 1409 + scrollback_current = 0; 1410 + } 1411 + 1412 + static __inline__ void ywrap_down(struct vc_data *vc, int count) 1413 + { 1414 + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 1415 + struct fbcon_ops *ops = info->fbcon_par; 1416 + struct fbcon_display *p = &fb_display[vc->vc_num]; 1417 + 1418 + p->yscroll -= count; 1419 + if (p->yscroll < 0) /* Deal with wrap */ 1420 + p->yscroll += p->vrows; 1421 + ops->var.xoffset = 0; 1422 + ops->var.yoffset = p->yscroll * vc->vc_font.height; 1423 + ops->var.vmode |= FB_VMODE_YWRAP; 1424 + ops->update_start(info); 1425 + scrollback_max -= count; 1426 + if (scrollback_max < 0) 1427 + scrollback_max = 0; 1428 + scrollback_current = 0; 1429 + } 1430 + 1431 + static __inline__ void ypan_up(struct vc_data *vc, int count) 1432 + { 1433 + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 1434 + struct fbcon_display *p = &fb_display[vc->vc_num]; 1435 + struct fbcon_ops *ops = info->fbcon_par; 1436 + 1437 + p->yscroll += count; 1438 + if (p->yscroll > p->vrows - vc->vc_rows) { 1439 + ops->bmove(vc, info, p->vrows - vc->vc_rows, 1440 + 0, 0, 0, vc->vc_rows, vc->vc_cols); 1441 + p->yscroll -= p->vrows - vc->vc_rows; 1442 + } 1443 + 1444 + ops->var.xoffset = 0; 1445 + ops->var.yoffset = p->yscroll * vc->vc_font.height; 1446 + ops->var.vmode &= ~FB_VMODE_YWRAP; 1447 + ops->update_start(info); 1448 + fbcon_clear_margins(vc, 1); 1449 + scrollback_max += count; 1450 + if (scrollback_max > scrollback_phys_max) 1451 + scrollback_max = scrollback_phys_max; 1452 + scrollback_current = 0; 1453 + } 1454 + 1455 + static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count) 1456 + { 1457 + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 1458 + struct fbcon_ops *ops = info->fbcon_par; 1459 + struct fbcon_display *p = &fb_display[vc->vc_num]; 1460 + 1461 + p->yscroll += count; 1462 + 1463 + if (p->yscroll > p->vrows - vc->vc_rows) { 1464 + p->yscroll -= p->vrows - vc->vc_rows; 1465 + fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t); 1466 + } 1467 + 1468 + ops->var.xoffset = 0; 1469 + ops->var.yoffset = p->yscroll * vc->vc_font.height; 1470 + ops->var.vmode &= ~FB_VMODE_YWRAP; 1471 + ops->update_start(info); 1472 + fbcon_clear_margins(vc, 1); 1473 + scrollback_max += count; 1474 + if (scrollback_max > scrollback_phys_max) 1475 + scrollback_max = scrollback_phys_max; 1476 + scrollback_current = 0; 1477 + } 1478 + 1479 + static __inline__ void ypan_down(struct vc_data *vc, int count) 1480 + { 1481 + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 1482 + struct fbcon_display *p = &fb_display[vc->vc_num]; 1483 + struct fbcon_ops *ops = info->fbcon_par; 1484 + 1485 + p->yscroll -= count; 1486 + if (p->yscroll < 0) { 1487 + ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows, 1488 + 0, vc->vc_rows, vc->vc_cols); 1489 + p->yscroll += p->vrows - vc->vc_rows; 1490 + } 1491 + 1492 + ops->var.xoffset = 0; 1493 + ops->var.yoffset = p->yscroll * vc->vc_font.height; 1494 + ops->var.vmode &= ~FB_VMODE_YWRAP; 1495 + ops->update_start(info); 1496 + fbcon_clear_margins(vc, 1); 1497 + scrollback_max -= count; 1498 + if (scrollback_max < 0) 1499 + scrollback_max = 0; 1500 + scrollback_current = 0; 1501 + } 1502 + 1503 + static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) 1504 + { 1505 + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 1506 + struct fbcon_ops *ops = info->fbcon_par; 1507 + struct fbcon_display *p = &fb_display[vc->vc_num]; 1508 + 1509 + p->yscroll -= count; 1510 + 1511 + if (p->yscroll < 0) { 1512 + p->yscroll += p->vrows - vc->vc_rows; 1513 + fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count); 1514 + } 1515 + 1516 + ops->var.xoffset = 0; 1517 + ops->var.yoffset = p->yscroll * vc->vc_font.height; 1518 + ops->var.vmode &= ~FB_VMODE_YWRAP; 1519 + ops->update_start(info); 1520 + fbcon_clear_margins(vc, 1); 1521 + scrollback_max -= count; 1522 + if (scrollback_max < 0) 1523 + scrollback_max = 0; 1524 + scrollback_current = 0; 1525 + } 1526 + 1527 + static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p, 1528 + int line, int count, int dy) 1529 + { 1530 + unsigned short *s = (unsigned short *) 1531 + (vc->vc_origin + vc->vc_size_row * line); 1532 + 1533 + while (count--) { 1534 + unsigned short *start = s; 1535 + unsigned short *le = advance_row(s, 1); 1536 + unsigned short c; 1537 + int x = 0; 1538 + unsigned short attr = 1; 1539 + 1540 + do { 1541 + c = scr_readw(s); 1542 + if (attr != (c & 0xff00)) { 1543 + attr = c & 0xff00; 1544 + if (s > start) { 1545 + fbcon_putcs(vc, start, s - start, 1546 + dy, x); 1547 + x += s - start; 1548 + start = s; 1549 + } 1550 + } 1551 + console_conditional_schedule(); 1552 + s++; 1553 + } while (s < le); 1554 + if (s > start) 1555 + fbcon_putcs(vc, start, s - start, dy, x); 1556 + console_conditional_schedule(); 1557 + dy++; 1558 + } 1559 + } 1560 + 1561 + static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info, 1562 + struct fbcon_display *p, int line, int count, int ycount) 1563 + { 1564 + int offset = ycount * vc->vc_cols; 1565 + unsigned short *d = (unsigned short *) 1566 + (vc->vc_origin + vc->vc_size_row * line); 1567 + unsigned short *s = d + offset; 1568 + struct fbcon_ops *ops = info->fbcon_par; 1569 + 1570 + while (count--) { 1571 + unsigned short *start = s; 1572 + unsigned short *le = advance_row(s, 1); 1573 + unsigned short c; 1574 + int x = 0; 1575 + 1576 + do { 1577 + c = scr_readw(s); 1578 + 1579 + if (c == scr_readw(d)) { 1580 + if (s > start) { 1581 + ops->bmove(vc, info, line + ycount, x, 1582 + line, x, 1, s-start); 1583 + x += s - start + 1; 1584 + start = s + 1; 1585 + } else { 1586 + x++; 1587 + start++; 1588 + } 1589 + } 1590 + 1591 + scr_writew(c, d); 1592 + console_conditional_schedule(); 1593 + s++; 1594 + d++; 1595 + } while (s < le); 1596 + if (s > start) 1597 + ops->bmove(vc, info, line + ycount, x, line, x, 1, 1598 + s-start); 1599 + console_conditional_schedule(); 1600 + if (ycount > 0) 1601 + line++; 1602 + else { 1603 + line--; 1604 + /* NOTE: We subtract two lines from these pointers */ 1605 + s -= vc->vc_size_row; 1606 + d -= vc->vc_size_row; 1607 + } 1608 + } 1609 + } 1610 + 1412 1611 static void fbcon_redraw(struct vc_data *vc, struct fbcon_display *p, 1413 1612 int line, int count, int offset) 1414 1613 { ··· 1687 1450 { 1688 1451 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 1689 1452 struct fbcon_display *p = &fb_display[vc->vc_num]; 1453 + int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; 1690 1454 1691 1455 if (fbcon_is_inactive(vc, info)) 1692 1456 return true; ··· 1704 1466 case SM_UP: 1705 1467 if (count > vc->vc_rows) /* Maximum realistic size */ 1706 1468 count = vc->vc_rows; 1707 - fbcon_redraw(vc, p, t, b - t - count, 1708 - count * vc->vc_cols); 1709 - fbcon_clear(vc, b - count, 0, count, vc->vc_cols); 1710 - scr_memsetw((unsigned short *) (vc->vc_origin + 1711 - vc->vc_size_row * 1712 - (b - count)), 1713 - vc->vc_video_erase_char, 1714 - vc->vc_size_row * count); 1715 - return true; 1469 + if (logo_shown >= 0) 1470 + goto redraw_up; 1471 + switch (p->scrollmode) { 1472 + case SCROLL_MOVE: 1473 + fbcon_redraw_blit(vc, info, p, t, b - t - count, 1474 + count); 1475 + fbcon_clear(vc, b - count, 0, count, vc->vc_cols); 1476 + scr_memsetw((unsigned short *) (vc->vc_origin + 1477 + vc->vc_size_row * 1478 + (b - count)), 1479 + vc->vc_video_erase_char, 1480 + vc->vc_size_row * count); 1481 + return true; 1482 + 1483 + case SCROLL_WRAP_MOVE: 1484 + if (b - t - count > 3 * vc->vc_rows >> 2) { 1485 + if (t > 0) 1486 + fbcon_bmove(vc, 0, 0, count, 0, t, 1487 + vc->vc_cols); 1488 + ywrap_up(vc, count); 1489 + if (vc->vc_rows - b > 0) 1490 + fbcon_bmove(vc, b - count, 0, b, 0, 1491 + vc->vc_rows - b, 1492 + vc->vc_cols); 1493 + } else if (info->flags & FBINFO_READS_FAST) 1494 + fbcon_bmove(vc, t + count, 0, t, 0, 1495 + b - t - count, vc->vc_cols); 1496 + else 1497 + goto redraw_up; 1498 + fbcon_clear(vc, b - count, 0, count, vc->vc_cols); 1499 + break; 1500 + 1501 + case SCROLL_PAN_REDRAW: 1502 + if ((p->yscroll + count <= 1503 + 2 * (p->vrows - vc->vc_rows)) 1504 + && ((!scroll_partial && (b - t == vc->vc_rows)) 1505 + || (scroll_partial 1506 + && (b - t - count > 1507 + 3 * vc->vc_rows >> 2)))) { 1508 + if (t > 0) 1509 + fbcon_redraw_move(vc, p, 0, t, count); 1510 + ypan_up_redraw(vc, t, count); 1511 + if (vc->vc_rows - b > 0) 1512 + fbcon_redraw_move(vc, p, b, 1513 + vc->vc_rows - b, b); 1514 + } else 1515 + fbcon_redraw_move(vc, p, t + count, b - t - count, t); 1516 + fbcon_clear(vc, b - count, 0, count, vc->vc_cols); 1517 + break; 1518 + 1519 + case SCROLL_PAN_MOVE: 1520 + if ((p->yscroll + count <= 1521 + 2 * (p->vrows - vc->vc_rows)) 1522 + && ((!scroll_partial && (b - t == vc->vc_rows)) 1523 + || (scroll_partial 1524 + && (b - t - count > 1525 + 3 * vc->vc_rows >> 2)))) { 1526 + if (t > 0) 1527 + fbcon_bmove(vc, 0, 0, count, 0, t, 1528 + vc->vc_cols); 1529 + ypan_up(vc, count); 1530 + if (vc->vc_rows - b > 0) 1531 + fbcon_bmove(vc, b - count, 0, b, 0, 1532 + vc->vc_rows - b, 1533 + vc->vc_cols); 1534 + } else if (info->flags & FBINFO_READS_FAST) 1535 + fbcon_bmove(vc, t + count, 0, t, 0, 1536 + b - t - count, vc->vc_cols); 1537 + else 1538 + goto redraw_up; 1539 + fbcon_clear(vc, b - count, 0, count, vc->vc_cols); 1540 + break; 1541 + 1542 + case SCROLL_REDRAW: 1543 + redraw_up: 1544 + fbcon_redraw(vc, p, t, b - t - count, 1545 + count * vc->vc_cols); 1546 + fbcon_clear(vc, b - count, 0, count, vc->vc_cols); 1547 + scr_memsetw((unsigned short *) (vc->vc_origin + 1548 + vc->vc_size_row * 1549 + (b - count)), 1550 + vc->vc_video_erase_char, 1551 + vc->vc_size_row * count); 1552 + return true; 1553 + } 1554 + break; 1716 1555 1717 1556 case SM_DOWN: 1718 1557 if (count > vc->vc_rows) /* Maximum realistic size */ 1719 1558 count = vc->vc_rows; 1720 - fbcon_redraw(vc, p, b - 1, b - t - count, 1721 - -count * vc->vc_cols); 1722 - fbcon_clear(vc, t, 0, count, vc->vc_cols); 1723 - scr_memsetw((unsigned short *) (vc->vc_origin + 1724 - vc->vc_size_row * 1725 - t), 1726 - vc->vc_video_erase_char, 1727 - vc->vc_size_row * count); 1728 - return true; 1559 + if (logo_shown >= 0) 1560 + goto redraw_down; 1561 + switch (p->scrollmode) { 1562 + case SCROLL_MOVE: 1563 + fbcon_redraw_blit(vc, info, p, b - 1, b - t - count, 1564 + -count); 1565 + fbcon_clear(vc, t, 0, count, vc->vc_cols); 1566 + scr_memsetw((unsigned short *) (vc->vc_origin + 1567 + vc->vc_size_row * 1568 + t), 1569 + vc->vc_video_erase_char, 1570 + vc->vc_size_row * count); 1571 + return true; 1572 + 1573 + case SCROLL_WRAP_MOVE: 1574 + if (b - t - count > 3 * vc->vc_rows >> 2) { 1575 + if (vc->vc_rows - b > 0) 1576 + fbcon_bmove(vc, b, 0, b - count, 0, 1577 + vc->vc_rows - b, 1578 + vc->vc_cols); 1579 + ywrap_down(vc, count); 1580 + if (t > 0) 1581 + fbcon_bmove(vc, count, 0, 0, 0, t, 1582 + vc->vc_cols); 1583 + } else if (info->flags & FBINFO_READS_FAST) 1584 + fbcon_bmove(vc, t, 0, t + count, 0, 1585 + b - t - count, vc->vc_cols); 1586 + else 1587 + goto redraw_down; 1588 + fbcon_clear(vc, t, 0, count, vc->vc_cols); 1589 + break; 1590 + 1591 + case SCROLL_PAN_MOVE: 1592 + if ((count - p->yscroll <= p->vrows - vc->vc_rows) 1593 + && ((!scroll_partial && (b - t == vc->vc_rows)) 1594 + || (scroll_partial 1595 + && (b - t - count > 1596 + 3 * vc->vc_rows >> 2)))) { 1597 + if (vc->vc_rows - b > 0) 1598 + fbcon_bmove(vc, b, 0, b - count, 0, 1599 + vc->vc_rows - b, 1600 + vc->vc_cols); 1601 + ypan_down(vc, count); 1602 + if (t > 0) 1603 + fbcon_bmove(vc, count, 0, 0, 0, t, 1604 + vc->vc_cols); 1605 + } else if (info->flags & FBINFO_READS_FAST) 1606 + fbcon_bmove(vc, t, 0, t + count, 0, 1607 + b - t - count, vc->vc_cols); 1608 + else 1609 + goto redraw_down; 1610 + fbcon_clear(vc, t, 0, count, vc->vc_cols); 1611 + break; 1612 + 1613 + case SCROLL_PAN_REDRAW: 1614 + if ((count - p->yscroll <= p->vrows - vc->vc_rows) 1615 + && ((!scroll_partial && (b - t == vc->vc_rows)) 1616 + || (scroll_partial 1617 + && (b - t - count > 1618 + 3 * vc->vc_rows >> 2)))) { 1619 + if (vc->vc_rows - b > 0) 1620 + fbcon_redraw_move(vc, p, b, vc->vc_rows - b, 1621 + b - count); 1622 + ypan_down_redraw(vc, t, count); 1623 + if (t > 0) 1624 + fbcon_redraw_move(vc, p, count, t, 0); 1625 + } else 1626 + fbcon_redraw_move(vc, p, t, b - t - count, t + count); 1627 + fbcon_clear(vc, t, 0, count, vc->vc_cols); 1628 + break; 1629 + 1630 + case SCROLL_REDRAW: 1631 + redraw_down: 1632 + fbcon_redraw(vc, p, b - 1, b - t - count, 1633 + -count * vc->vc_cols); 1634 + fbcon_clear(vc, t, 0, count, vc->vc_cols); 1635 + scr_memsetw((unsigned short *) (vc->vc_origin + 1636 + vc->vc_size_row * 1637 + t), 1638 + vc->vc_video_erase_char, 1639 + vc->vc_size_row * count); 1640 + return true; 1641 + } 1729 1642 } 1730 1643 return false; 1644 + } 1645 + 1646 + 1647 + static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, 1648 + int height, int width) 1649 + { 1650 + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 1651 + struct fbcon_display *p = &fb_display[vc->vc_num]; 1652 + 1653 + if (fbcon_is_inactive(vc, info)) 1654 + return; 1655 + 1656 + if (!width || !height) 1657 + return; 1658 + 1659 + /* Split blits that cross physical y_wrap case. 1660 + * Pathological case involves 4 blits, better to use recursive 1661 + * code rather than unrolled case 1662 + * 1663 + * Recursive invocations don't need to erase the cursor over and 1664 + * over again, so we use fbcon_bmove_rec() 1665 + */ 1666 + fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width, 1667 + p->vrows - p->yscroll); 1668 + } 1669 + 1670 + static void fbcon_bmove_rec(struct vc_data *vc, struct fbcon_display *p, int sy, int sx, 1671 + int dy, int dx, int height, int width, u_int y_break) 1672 + { 1673 + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 1674 + struct fbcon_ops *ops = info->fbcon_par; 1675 + u_int b; 1676 + 1677 + if (sy < y_break && sy + height > y_break) { 1678 + b = y_break - sy; 1679 + if (dy < sy) { /* Avoid trashing self */ 1680 + fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, 1681 + y_break); 1682 + fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, 1683 + height - b, width, y_break); 1684 + } else { 1685 + fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, 1686 + height - b, width, y_break); 1687 + fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, 1688 + y_break); 1689 + } 1690 + return; 1691 + } 1692 + 1693 + if (dy < y_break && dy + height > y_break) { 1694 + b = y_break - dy; 1695 + if (dy < sy) { /* Avoid trashing self */ 1696 + fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, 1697 + y_break); 1698 + fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, 1699 + height - b, width, y_break); 1700 + } else { 1701 + fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, 1702 + height - b, width, y_break); 1703 + fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, 1704 + y_break); 1705 + } 1706 + return; 1707 + } 1708 + ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, 1709 + height, width); 1731 1710 } 1732 1711 1733 1712 static void updatescrollmode(struct fbcon_display *p, ··· 2119 1664 2120 1665 updatescrollmode(p, info, vc); 2121 1666 2122 - scrollback_phys_max = 0; 1667 + switch (p->scrollmode) { 1668 + case SCROLL_WRAP_MOVE: 1669 + scrollback_phys_max = p->vrows - vc->vc_rows; 1670 + break; 1671 + case SCROLL_PAN_MOVE: 1672 + case SCROLL_PAN_REDRAW: 1673 + scrollback_phys_max = p->vrows - 2 * vc->vc_rows; 1674 + if (scrollback_phys_max < 0) 1675 + scrollback_phys_max = 0; 1676 + break; 1677 + default: 1678 + scrollback_phys_max = 0; 1679 + break; 1680 + } 1681 + 2123 1682 scrollback_max = 0; 2124 1683 scrollback_current = 0; 2125 1684
+59
drivers/video/fbdev/core/fbcon.h
··· 29 29 /* Filled in by the low-level console driver */ 30 30 const u_char *fontdata; 31 31 int userfont; /* != 0 if fontdata kmalloc()ed */ 32 + u_short scrollmode; /* Scroll Method */ 32 33 u_short inverse; /* != 0 text black on white as default */ 33 34 short yscroll; /* Hardware scrolling */ 34 35 int vrows; /* number of virtual rows */ ··· 52 51 }; 53 52 54 53 struct fbcon_ops { 54 + void (*bmove)(struct vc_data *vc, struct fb_info *info, int sy, 55 + int sx, int dy, int dx, int height, int width); 55 56 void (*clear)(struct vc_data *vc, struct fb_info *info, int sy, 56 57 int sx, int height, int width); 57 58 void (*putcs)(struct vc_data *vc, struct fb_info *info, ··· 151 148 152 149 #define attr_bgcol_ec(bgshift, vc, info) attr_col_ec(bgshift, vc, info, 0) 153 150 #define attr_fgcol_ec(fgshift, vc, info) attr_col_ec(fgshift, vc, info, 1) 151 + 152 + /* 153 + * Scroll Method 154 + */ 155 + 156 + /* There are several methods fbcon can use to move text around the screen: 157 + * 158 + * Operation Pan Wrap 159 + *--------------------------------------------- 160 + * SCROLL_MOVE copyarea No No 161 + * SCROLL_PAN_MOVE copyarea Yes No 162 + * SCROLL_WRAP_MOVE copyarea No Yes 163 + * SCROLL_REDRAW imageblit No No 164 + * SCROLL_PAN_REDRAW imageblit Yes No 165 + * SCROLL_WRAP_REDRAW imageblit No Yes 166 + * 167 + * (SCROLL_WRAP_REDRAW is not implemented yet) 168 + * 169 + * In general, fbcon will choose the best scrolling 170 + * method based on the rule below: 171 + * 172 + * Pan/Wrap > accel imageblit > accel copyarea > 173 + * soft imageblit > (soft copyarea) 174 + * 175 + * Exception to the rule: Pan + accel copyarea is 176 + * preferred over Pan + accel imageblit. 177 + * 178 + * The above is typical for PCI/AGP cards. Unless 179 + * overridden, fbcon will never use soft copyarea. 180 + * 181 + * If you need to override the above rule, set the 182 + * appropriate flags in fb_info->flags. For example, 183 + * to prefer copyarea over imageblit, set 184 + * FBINFO_READS_FAST. 185 + * 186 + * Other notes: 187 + * + use the hardware engine to move the text 188 + * (hw-accelerated copyarea() and fillrect()) 189 + * + use hardware-supported panning on a large virtual screen 190 + * + amifb can not only pan, but also wrap the display by N lines 191 + * (i.e. visible line i = physical line (i+N) % yres). 192 + * + read what's already rendered on the screen and 193 + * write it in a different place (this is cfb_copyarea()) 194 + * + re-render the text to the screen 195 + * 196 + * Whether to use wrapping or panning can only be figured out at 197 + * runtime (when we know whether our font height is a multiple 198 + * of the pan/wrap step) 199 + * 200 + */ 201 + 202 + #define SCROLL_MOVE 0x001 203 + #define SCROLL_PAN_MOVE 0x002 204 + #define SCROLL_WRAP_MOVE 0x003 205 + #define SCROLL_REDRAW 0x004 206 + #define SCROLL_PAN_REDRAW 0x005 154 207 155 208 #ifdef CONFIG_FB_TILEBLITTING 156 209 extern void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info);
+24 -4
drivers/video/fbdev/core/fbcon_ccw.c
··· 59 59 } 60 60 } 61 61 62 + 63 + static void ccw_bmove(struct vc_data *vc, struct fb_info *info, int sy, 64 + int sx, int dy, int dx, int height, int width) 65 + { 66 + struct fbcon_ops *ops = info->fbcon_par; 67 + struct fb_copyarea area; 68 + u32 vyres = GETVYRES(ops->p->scrollmode, info); 69 + 70 + area.sx = sy * vc->vc_font.height; 71 + area.sy = vyres - ((sx + width) * vc->vc_font.width); 72 + area.dx = dy * vc->vc_font.height; 73 + area.dy = vyres - ((dx + width) * vc->vc_font.width); 74 + area.width = height * vc->vc_font.height; 75 + area.height = width * vc->vc_font.width; 76 + 77 + info->fbops->fb_copyarea(info, &area); 78 + } 79 + 62 80 static void ccw_clear(struct vc_data *vc, struct fb_info *info, int sy, 63 81 int sx, int height, int width) 64 82 { 83 + struct fbcon_ops *ops = info->fbcon_par; 65 84 struct fb_fillrect region; 66 85 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; 67 - u32 vyres = info->var.yres; 86 + u32 vyres = GETVYRES(ops->p->scrollmode, info); 68 87 69 88 region.color = attr_bgcol_ec(bgshift,vc,info); 70 89 region.dx = sy * vc->vc_font.height; ··· 140 121 u32 cnt, pitch, size; 141 122 u32 attribute = get_attribute(info, scr_readw(s)); 142 123 u8 *dst, *buf = NULL; 143 - u32 vyres = info->var.yres; 124 + u32 vyres = GETVYRES(ops->p->scrollmode, info); 144 125 145 126 if (!ops->fontbuffer) 146 127 return; ··· 229 210 int attribute, use_sw = vc->vc_cursor_type & CUR_SW; 230 211 int err = 1, dx, dy; 231 212 char *src; 232 - u32 vyres = info->var.yres; 213 + u32 vyres = GETVYRES(ops->p->scrollmode, info); 233 214 234 215 if (!ops->fontbuffer) 235 216 return; ··· 387 368 { 388 369 struct fbcon_ops *ops = info->fbcon_par; 389 370 u32 yoffset; 390 - u32 vyres = info->var.yres; 371 + u32 vyres = GETVYRES(ops->p->scrollmode, info); 391 372 int err; 392 373 393 374 yoffset = (vyres - info->var.yres) - ops->var.xoffset; ··· 402 383 403 384 void fbcon_rotate_ccw(struct fbcon_ops *ops) 404 385 { 386 + ops->bmove = ccw_bmove; 405 387 ops->clear = ccw_clear; 406 388 ops->putcs = ccw_putcs; 407 389 ops->clear_margins = ccw_clear_margins;
+24 -4
drivers/video/fbdev/core/fbcon_cw.c
··· 44 44 } 45 45 } 46 46 47 + 48 + static void cw_bmove(struct vc_data *vc, struct fb_info *info, int sy, 49 + int sx, int dy, int dx, int height, int width) 50 + { 51 + struct fbcon_ops *ops = info->fbcon_par; 52 + struct fb_copyarea area; 53 + u32 vxres = GETVXRES(ops->p->scrollmode, info); 54 + 55 + area.sx = vxres - ((sy + height) * vc->vc_font.height); 56 + area.sy = sx * vc->vc_font.width; 57 + area.dx = vxres - ((dy + height) * vc->vc_font.height); 58 + area.dy = dx * vc->vc_font.width; 59 + area.width = height * vc->vc_font.height; 60 + area.height = width * vc->vc_font.width; 61 + 62 + info->fbops->fb_copyarea(info, &area); 63 + } 64 + 47 65 static void cw_clear(struct vc_data *vc, struct fb_info *info, int sy, 48 66 int sx, int height, int width) 49 67 { 68 + struct fbcon_ops *ops = info->fbcon_par; 50 69 struct fb_fillrect region; 51 70 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; 52 - u32 vxres = info->var.xres; 71 + u32 vxres = GETVXRES(ops->p->scrollmode, info); 53 72 54 73 region.color = attr_bgcol_ec(bgshift,vc,info); 55 74 region.dx = vxres - ((sy + height) * vc->vc_font.height); ··· 125 106 u32 cnt, pitch, size; 126 107 u32 attribute = get_attribute(info, scr_readw(s)); 127 108 u8 *dst, *buf = NULL; 128 - u32 vxres = info->var.xres; 109 + u32 vxres = GETVXRES(ops->p->scrollmode, info); 129 110 130 111 if (!ops->fontbuffer) 131 112 return; ··· 212 193 int attribute, use_sw = vc->vc_cursor_type & CUR_SW; 213 194 int err = 1, dx, dy; 214 195 char *src; 215 - u32 vxres = info->var.xres; 196 + u32 vxres = GETVXRES(ops->p->scrollmode, info); 216 197 217 198 if (!ops->fontbuffer) 218 199 return; ··· 369 350 static int cw_update_start(struct fb_info *info) 370 351 { 371 352 struct fbcon_ops *ops = info->fbcon_par; 372 - u32 vxres = info->var.xres; 353 + u32 vxres = GETVXRES(ops->p->scrollmode, info); 373 354 u32 xoffset; 374 355 int err; 375 356 ··· 385 366 386 367 void fbcon_rotate_cw(struct fbcon_ops *ops) 387 368 { 369 + ops->bmove = cw_bmove; 388 370 ops->clear = cw_clear; 389 371 ops->putcs = cw_putcs; 390 372 ops->clear_margins = cw_clear_margins;
+9
drivers/video/fbdev/core/fbcon_rotate.h
··· 11 11 #ifndef _FBCON_ROTATE_H 12 12 #define _FBCON_ROTATE_H 13 13 14 + #define GETVYRES(s,i) ({ \ 15 + (s == SCROLL_REDRAW || s == SCROLL_MOVE) ? \ 16 + (i)->var.yres : (i)->var.yres_virtual; }) 17 + 18 + #define GETVXRES(s,i) ({ \ 19 + (s == SCROLL_REDRAW || s == SCROLL_MOVE || !(i)->fix.xpanstep) ? \ 20 + (i)->var.xres : (i)->var.xres_virtual; }) 21 + 22 + 14 23 static inline int pattern_test_bit(u32 x, u32 y, u32 pitch, const char *pat) 15 24 { 16 25 u32 tmp = (y * pitch) + x, index = tmp / 8, bit = tmp % 8;
+29 -8
drivers/video/fbdev/core/fbcon_ud.c
··· 44 44 } 45 45 } 46 46 47 + 48 + static void ud_bmove(struct vc_data *vc, struct fb_info *info, int sy, 49 + int sx, int dy, int dx, int height, int width) 50 + { 51 + struct fbcon_ops *ops = info->fbcon_par; 52 + struct fb_copyarea area; 53 + u32 vyres = GETVYRES(ops->p->scrollmode, info); 54 + u32 vxres = GETVXRES(ops->p->scrollmode, info); 55 + 56 + area.sy = vyres - ((sy + height) * vc->vc_font.height); 57 + area.sx = vxres - ((sx + width) * vc->vc_font.width); 58 + area.dy = vyres - ((dy + height) * vc->vc_font.height); 59 + area.dx = vxres - ((dx + width) * vc->vc_font.width); 60 + area.height = height * vc->vc_font.height; 61 + area.width = width * vc->vc_font.width; 62 + 63 + info->fbops->fb_copyarea(info, &area); 64 + } 65 + 47 66 static void ud_clear(struct vc_data *vc, struct fb_info *info, int sy, 48 67 int sx, int height, int width) 49 68 { 69 + struct fbcon_ops *ops = info->fbcon_par; 50 70 struct fb_fillrect region; 51 71 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; 52 - u32 vyres = info->var.yres; 53 - u32 vxres = info->var.xres; 72 + u32 vyres = GETVYRES(ops->p->scrollmode, info); 73 + u32 vxres = GETVXRES(ops->p->scrollmode, info); 54 74 55 75 region.color = attr_bgcol_ec(bgshift,vc,info); 56 76 region.dy = vyres - ((sy + height) * vc->vc_font.height); ··· 162 142 u32 mod = vc->vc_font.width % 8, cnt, pitch, size; 163 143 u32 attribute = get_attribute(info, scr_readw(s)); 164 144 u8 *dst, *buf = NULL; 165 - u32 vyres = info->var.yres; 166 - u32 vxres = info->var.xres; 145 + u32 vyres = GETVYRES(ops->p->scrollmode, info); 146 + u32 vxres = GETVXRES(ops->p->scrollmode, info); 167 147 168 148 if (!ops->fontbuffer) 169 149 return; ··· 259 239 int attribute, use_sw = vc->vc_cursor_type & CUR_SW; 260 240 int err = 1, dx, dy; 261 241 char *src; 262 - u32 vyres = info->var.yres; 263 - u32 vxres = info->var.xres; 242 + u32 vyres = GETVYRES(ops->p->scrollmode, info); 243 + u32 vxres = GETVXRES(ops->p->scrollmode, info); 264 244 265 245 if (!ops->fontbuffer) 266 246 return; ··· 410 390 { 411 391 struct fbcon_ops *ops = info->fbcon_par; 412 392 int xoffset, yoffset; 413 - u32 vyres = info->var.yres; 414 - u32 vxres = info->var.xres; 393 + u32 vyres = GETVYRES(ops->p->scrollmode, info); 394 + u32 vxres = GETVXRES(ops->p->scrollmode, info); 415 395 int err; 416 396 417 397 xoffset = vxres - info->var.xres - ops->var.xoffset; ··· 429 409 430 410 void fbcon_rotate_ud(struct fbcon_ops *ops) 431 411 { 412 + ops->bmove = ud_bmove; 432 413 ops->clear = ud_clear; 433 414 ops->putcs = ud_putcs; 434 415 ops->clear_margins = ud_clear_margins;
+16
drivers/video/fbdev/core/tileblit.c
··· 16 16 #include <asm/types.h> 17 17 #include "fbcon.h" 18 18 19 + static void tile_bmove(struct vc_data *vc, struct fb_info *info, int sy, 20 + int sx, int dy, int dx, int height, int width) 21 + { 22 + struct fb_tilearea area; 23 + 24 + area.sx = sx; 25 + area.sy = sy; 26 + area.dx = dx; 27 + area.dy = dy; 28 + area.height = height; 29 + area.width = width; 30 + 31 + info->tileops->fb_tilecopy(info, &area); 32 + } 33 + 19 34 static void tile_clear(struct vc_data *vc, struct fb_info *info, int sy, 20 35 int sx, int height, int width) 21 36 { ··· 133 118 struct fb_tilemap map; 134 119 struct fbcon_ops *ops = info->fbcon_par; 135 120 121 + ops->bmove = tile_bmove; 136 122 ops->clear = tile_clear; 137 123 ops->putcs = tile_putcs; 138 124 ops->clear_margins = tile_clear_margins;
+6 -6
drivers/video/fbdev/skeletonfb.c
··· 505 505 } 506 506 507 507 /** 508 - * xxxfb_copyarea - OBSOLETE function. 508 + * xxxfb_copyarea - REQUIRED function. Can use generic routines if 509 + * non acclerated hardware and packed pixel based. 509 510 * Copies one area of the screen to another area. 510 - * Will be deleted in a future version 511 511 * 512 512 * @info: frame buffer structure that represents a single frame buffer 513 513 * @area: Structure providing the data to copy the framebuffer contents 514 514 * from one region to another. 515 515 * 516 - * This drawing operation copied a rectangular area from one area of the 516 + * This drawing operation copies a rectangular area from one area of the 517 517 * screen to another area. 518 518 */ 519 519 void xxxfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) ··· 645 645 .fb_setcolreg = xxxfb_setcolreg, 646 646 .fb_blank = xxxfb_blank, 647 647 .fb_pan_display = xxxfb_pan_display, 648 - .fb_fillrect = xxxfb_fillrect, /* Needed !!! */ 649 - .fb_copyarea = xxxfb_copyarea, /* Obsolete */ 650 - .fb_imageblit = xxxfb_imageblit, /* Needed !!! */ 648 + .fb_fillrect = xxxfb_fillrect, /* Needed !!! */ 649 + .fb_copyarea = xxxfb_copyarea, /* Needed !!! */ 650 + .fb_imageblit = xxxfb_imageblit, /* Needed !!! */ 651 651 .fb_cursor = xxxfb_cursor, /* Optional !!! */ 652 652 .fb_sync = xxxfb_sync, 653 653 .fb_ioctl = xxxfb_ioctl,
+1 -1
include/linux/fb.h
··· 262 262 263 263 /* Draws a rectangle */ 264 264 void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect); 265 - /* Copy data from area to another. Obsolete. */ 265 + /* Copy data from area to another */ 266 266 void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region); 267 267 /* Draws a image to the display */ 268 268 void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);