aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/console
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/console')
-rw-r--r--drivers/video/console/Kconfig10
-rw-r--r--drivers/video/console/Makefile4
-rw-r--r--drivers/video/console/bitblit.c46
-rw-r--r--drivers/video/console/fbcon.c375
-rw-r--r--drivers/video/console/fbcon.h62
-rw-r--r--drivers/video/console/fbcon_ccw.c428
-rw-r--r--drivers/video/console/fbcon_cw.c412
-rw-r--r--drivers/video/console/fbcon_rotate.c117
-rw-r--r--drivers/video/console/fbcon_rotate.h105
-rw-r--r--drivers/video/console/fbcon_ud.c454
-rw-r--r--drivers/video/console/tileblit.c13
11 files changed, 1888 insertions, 138 deletions
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index fadf7c5d216e..94c5f1392cce 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -101,6 +101,16 @@ config FRAMEBUFFER_CONSOLE
101 help 101 help
102 Low-level framebuffer-based console driver. 102 Low-level framebuffer-based console driver.
103 103
104config FRAMEBUFFER_CONSOLE_ROTATION
105 bool "Framebuffer Console Rotation"
106 depends on FRAMEBUFFER_CONSOLE
107 help
108 Enable display rotation for the framebuffer console. This is done
109 in software and may be significantly slower than a normally oriented
110 display. Note that the rotation is done at the console level only
111 such that other users of the framebuffer will remain normally
112 oriented.
113
104config STI_CONSOLE 114config STI_CONSOLE
105 tristate "STI text console" 115 tristate "STI text console"
106 depends on PARISC 116 depends on PARISC
diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
index 5222628accce..fed600c9ca55 100644
--- a/drivers/video/console/Makefile
+++ b/drivers/video/console/Makefile
@@ -31,6 +31,10 @@ obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon.o bitblit.o font.o softcursor.o
31ifeq ($(CONFIG_FB_TILEBLITTING),y) 31ifeq ($(CONFIG_FB_TILEBLITTING),y)
32obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += tileblit.o 32obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += tileblit.o
33endif 33endif
34ifeq ($(CONFIG_FRAMEBUFFER_CONSOLE_ROTATION),y)
35obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon_rotate.o fbcon_cw.o fbcon_ud.o \
36 fbcon_ccw.o
37endif
34 38
35obj-$(CONFIG_FB_STI) += sticore.o font.o 39obj-$(CONFIG_FB_STI) += sticore.o font.o
36 40
diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c
index 67857b3cfc8b..e65fc3ef7630 100644
--- a/drivers/video/console/bitblit.c
+++ b/drivers/video/console/bitblit.c
@@ -22,35 +22,6 @@
22/* 22/*
23 * Accelerated handlers. 23 * Accelerated handlers.
24 */ 24 */
25#define FBCON_ATTRIBUTE_UNDERLINE 1
26#define FBCON_ATTRIBUTE_REVERSE 2
27#define FBCON_ATTRIBUTE_BOLD 4
28
29static inline int real_y(struct display *p, int ypos)
30{
31 int rows = p->vrows;
32
33 ypos += p->yscroll;
34 return ypos < rows ? ypos : ypos - rows;
35}
36
37
38static inline int get_attribute(struct fb_info *info, u16 c)
39{
40 int attribute = 0;
41
42 if (fb_get_color_depth(&info->var, &info->fix) == 1) {
43 if (attr_underline(c))
44 attribute |= FBCON_ATTRIBUTE_UNDERLINE;
45 if (attr_reverse(c))
46 attribute |= FBCON_ATTRIBUTE_REVERSE;
47 if (attr_bold(c))
48 attribute |= FBCON_ATTRIBUTE_BOLD;
49 }
50
51 return attribute;
52}
53
54static inline void update_attr(u8 *dst, u8 *src, int attribute, 25static inline void update_attr(u8 *dst, u8 *src, int attribute,
55 struct vc_data *vc) 26 struct vc_data *vc)
56{ 27{
@@ -418,6 +389,18 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info,
418 ops->cursor_reset = 0; 389 ops->cursor_reset = 0;
419} 390}
420 391
392static int bit_update_start(struct fb_info *info)
393{
394 struct fbcon_ops *ops = info->fbcon_par;
395 int err;
396
397 err = fb_pan_display(info, &ops->var);
398 ops->var.xoffset = info->var.xoffset;
399 ops->var.yoffset = info->var.yoffset;
400 ops->var.vmode = info->var.vmode;
401 return err;
402}
403
421void fbcon_set_bitops(struct fbcon_ops *ops) 404void fbcon_set_bitops(struct fbcon_ops *ops)
422{ 405{
423 ops->bmove = bit_bmove; 406 ops->bmove = bit_bmove;
@@ -425,6 +408,11 @@ void fbcon_set_bitops(struct fbcon_ops *ops)
425 ops->putcs = bit_putcs; 408 ops->putcs = bit_putcs;
426 ops->clear_margins = bit_clear_margins; 409 ops->clear_margins = bit_clear_margins;
427 ops->cursor = bit_cursor; 410 ops->cursor = bit_cursor;
411 ops->update_start = bit_update_start;
412 ops->rotate_font = NULL;
413
414 if (ops->rotate)
415 fbcon_set_rotate(ops);
428} 416}
429 417
430EXPORT_SYMBOL(fbcon_set_bitops); 418EXPORT_SYMBOL(fbcon_set_bitops);
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 3cf1b61ff1f8..e7802ffe549a 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -107,6 +107,8 @@ enum {
107}; 107};
108 108
109struct display fb_display[MAX_NR_CONSOLES]; 109struct display fb_display[MAX_NR_CONSOLES];
110EXPORT_SYMBOL(fb_display);
111
110static signed char con2fb_map[MAX_NR_CONSOLES]; 112static signed char con2fb_map[MAX_NR_CONSOLES];
111static signed char con2fb_map_boot[MAX_NR_CONSOLES]; 113static signed char con2fb_map_boot[MAX_NR_CONSOLES];
112static int logo_height; 114static int logo_height;
@@ -130,6 +132,9 @@ static char fontname[40];
130/* current fb_info */ 132/* current fb_info */
131static int info_idx = -1; 133static int info_idx = -1;
132 134
135/* console rotation */
136static int rotate;
137
133static const struct consw fb_con; 138static const struct consw fb_con;
134 139
135#define CM_SOFTBACK (8) 140#define CM_SOFTBACK (8)
@@ -176,7 +181,6 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines);
176/* 181/*
177 * Internal routines 182 * Internal routines
178 */ 183 */
179static __inline__ int real_y(struct display *p, int ypos);
180static __inline__ void ywrap_up(struct vc_data *vc, int count); 184static __inline__ void ywrap_up(struct vc_data *vc, int count);
181static __inline__ void ywrap_down(struct vc_data *vc, int count); 185static __inline__ void ywrap_down(struct vc_data *vc, int count);
182static __inline__ void ypan_up(struct vc_data *vc, int count); 186static __inline__ void ypan_up(struct vc_data *vc, int count);
@@ -189,6 +193,8 @@ static void fbcon_preset_disp(struct fb_info *info, struct fb_var_screeninfo *va
189 int unit); 193 int unit);
190static void fbcon_redraw_move(struct vc_data *vc, struct display *p, 194static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
191 int line, int count, int dy); 195 int line, int count, int dy);
196static void fbcon_modechanged(struct fb_info *info);
197static void fbcon_set_all_vcs(struct fb_info *info);
192 198
193#ifdef CONFIG_MAC 199#ifdef CONFIG_MAC
194/* 200/*
@@ -203,6 +209,88 @@ static irqreturn_t fb_vbl_detect(int irq, void *dummy, struct pt_regs *fp)
203} 209}
204#endif 210#endif
205 211
212#ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
213static inline void fbcon_set_rotation(struct fb_info *info, struct display *p)
214{
215 struct fbcon_ops *ops = info->fbcon_par;
216
217 if (!(info->flags & FBINFO_MISC_TILEBLITTING) &&
218 p->con_rotate < 4)
219 ops->rotate = p->con_rotate;
220 else
221 ops->rotate = 0;
222}
223
224static void fbcon_rotate(struct fb_info *info, u32 rotate)
225{
226 struct fbcon_ops *ops= info->fbcon_par;
227 struct fb_info *fb_info;
228
229 if (!ops || ops->currcon == -1)
230 return;
231
232 fb_info = registered_fb[con2fb_map[ops->currcon]];
233
234 if (info == fb_info) {
235 struct display *p = &fb_display[ops->currcon];
236
237 if (rotate < 4)
238 p->con_rotate = rotate;
239 else
240 p->con_rotate = 0;
241
242 fbcon_modechanged(info);
243 }
244}
245
246static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
247{
248 struct fbcon_ops *ops = info->fbcon_par;
249 struct vc_data *vc;
250 struct display *p;
251 int i;
252
253 if (!ops || ops->currcon < 0 || rotate > 3)
254 return;
255
256 for (i = 0; i < MAX_NR_CONSOLES; i++) {
257 vc = vc_cons[i].d;
258 if (!vc || vc->vc_mode != KD_TEXT ||
259 registered_fb[con2fb_map[i]] != info)
260 continue;
261
262 p = &fb_display[vc->vc_num];
263 p->con_rotate = rotate;
264 }
265
266 fbcon_set_all_vcs(info);
267}
268#else
269static inline void fbcon_set_rotation(struct fb_info *info, struct display *p)
270{
271 struct fbcon_ops *ops = info->fbcon_par;
272
273 ops->rotate = FB_ROTATE_UR;
274}
275
276static void fbcon_rotate(struct fb_info *info, u32 rotate)
277{
278 return;
279}
280
281static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
282{
283 return;
284}
285#endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */
286
287static int fbcon_get_rotate(struct fb_info *info)
288{
289 struct fbcon_ops *ops = info->fbcon_par;
290
291 return (ops) ? ops->rotate : 0;
292}
293
206static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info) 294static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
207{ 295{
208 struct fbcon_ops *ops = info->fbcon_par; 296 struct fbcon_ops *ops = info->fbcon_par;
@@ -422,6 +510,14 @@ static int __init fb_console_setup(char *this_opt)
422 last_fb_vc = simple_strtoul(options, &options, 10) - 1; 510 last_fb_vc = simple_strtoul(options, &options, 10) - 1;
423 fbcon_is_default = 0; 511 fbcon_is_default = 0;
424 } 512 }
513
514 if (!strncmp(options, "rotate:", 7)) {
515 options += 7;
516 if (*options)
517 rotate = simple_strtoul(options, &options, 0);
518 if (rotate > 3)
519 rotate = 0;
520 }
425 } 521 }
426 return 0; 522 return 0;
427} 523}
@@ -480,6 +576,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
480 int cols, int rows, int new_cols, int new_rows) 576 int cols, int rows, int new_cols, int new_rows)
481{ 577{
482 /* Need to make room for the logo */ 578 /* Need to make room for the logo */
579 struct fbcon_ops *ops = info->fbcon_par;
483 int cnt, erase = vc->vc_video_erase_char, step; 580 int cnt, erase = vc->vc_video_erase_char, step;
484 unsigned short *save = NULL, *r, *q; 581 unsigned short *save = NULL, *r, *q;
485 582
@@ -489,7 +586,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
489 */ 586 */
490 if (fb_get_color_depth(&info->var, &info->fix) == 1) 587 if (fb_get_color_depth(&info->var, &info->fix) == 1)
491 erase &= ~0x400; 588 erase &= ~0x400;
492 logo_height = fb_prepare_logo(info); 589 logo_height = fb_prepare_logo(info, ops->rotate);
493 logo_lines = (logo_height + vc->vc_font.height - 1) / 590 logo_lines = (logo_height + vc->vc_font.height - 1) /
494 vc->vc_font.height; 591 vc->vc_font.height;
495 q = (unsigned short *) (vc->vc_origin + 592 q = (unsigned short *) (vc->vc_origin +
@@ -558,16 +655,24 @@ static void set_blitting_type(struct vc_data *vc, struct fb_info *info,
558 655
559 if ((info->flags & FBINFO_MISC_TILEBLITTING)) 656 if ((info->flags & FBINFO_MISC_TILEBLITTING))
560 fbcon_set_tileops(vc, info, p, ops); 657 fbcon_set_tileops(vc, info, p, ops);
561 else 658 else {
659 struct display *disp;
660
661 disp = (p) ? p : &fb_display[vc->vc_num];
662 fbcon_set_rotation(info, disp);
562 fbcon_set_bitops(ops); 663 fbcon_set_bitops(ops);
664 }
563} 665}
564#else 666#else
565static void set_blitting_type(struct vc_data *vc, struct fb_info *info, 667static void set_blitting_type(struct vc_data *vc, struct fb_info *info,
566 struct display *p) 668 struct display *p)
567{ 669{
568 struct fbcon_ops *ops = info->fbcon_par; 670 struct fbcon_ops *ops = info->fbcon_par;
671 struct display *disp;
569 672
570 info->flags &= ~FBINFO_MISC_TILEBLITTING; 673 info->flags &= ~FBINFO_MISC_TILEBLITTING;
674 disp = (p) ? p : &fb_display[vc->vc_num];
675 fbcon_set_rotation(info, disp);
571 fbcon_set_bitops(ops); 676 fbcon_set_bitops(ops);
572} 677}
573#endif /* CONFIG_MISC_TILEBLITTING */ 678#endif /* CONFIG_MISC_TILEBLITTING */
@@ -627,6 +732,7 @@ static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo,
627 fbcon_del_cursor_timer(oldinfo); 732 fbcon_del_cursor_timer(oldinfo);
628 kfree(ops->cursor_state.mask); 733 kfree(ops->cursor_state.mask);
629 kfree(ops->cursor_data); 734 kfree(ops->cursor_data);
735 kfree(ops->fontbuffer);
630 kfree(oldinfo->fbcon_par); 736 kfree(oldinfo->fbcon_par);
631 oldinfo->fbcon_par = NULL; 737 oldinfo->fbcon_par = NULL;
632 module_put(oldinfo->fbops->owner); 738 module_put(oldinfo->fbops->owner);
@@ -827,7 +933,9 @@ static const char *fbcon_startup(void)
827 memset(ops, 0, sizeof(struct fbcon_ops)); 933 memset(ops, 0, sizeof(struct fbcon_ops));
828 ops->currcon = -1; 934 ops->currcon = -1;
829 ops->graphics = 1; 935 ops->graphics = 1;
936 ops->cur_rotate = -1;
830 info->fbcon_par = ops; 937 info->fbcon_par = ops;
938 p->con_rotate = rotate;
831 set_blitting_type(vc, info, NULL); 939 set_blitting_type(vc, info, NULL);
832 940
833 if (info->fix.type != FB_TYPE_TEXT) { 941 if (info->fix.type != FB_TYPE_TEXT) {
@@ -866,8 +974,10 @@ static const char *fbcon_startup(void)
866 vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */ 974 vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */
867 } 975 }
868 976
869 cols = info->var.xres / vc->vc_font.width; 977 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
870 rows = info->var.yres / vc->vc_font.height; 978 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
979 cols /= vc->vc_font.width;
980 rows /= vc->vc_font.height;
871 vc_resize(vc, cols, rows); 981 vc_resize(vc, cols, rows);
872 982
873 DPRINTK("mode: %s\n", info->fix.id); 983 DPRINTK("mode: %s\n", info->fix.id);
@@ -953,8 +1063,6 @@ static void fbcon_init(struct vc_data *vc, int init)
953 (info->fix.type == FB_TYPE_TEXT)) 1063 (info->fix.type == FB_TYPE_TEXT))
954 logo = 0; 1064 logo = 0;
955 1065
956 info->var.xoffset = info->var.yoffset = p->yscroll = 0; /* reset wrap/pan */
957
958 if (var_to_display(p, &info->var, info)) 1066 if (var_to_display(p, &info->var, info))
959 return; 1067 return;
960 1068
@@ -986,13 +1094,18 @@ static void fbcon_init(struct vc_data *vc, int init)
986 if (!*vc->vc_uni_pagedir_loc) 1094 if (!*vc->vc_uni_pagedir_loc)
987 con_copy_unimap(vc, svc); 1095 con_copy_unimap(vc, svc);
988 1096
1097 ops = info->fbcon_par;
1098 p->con_rotate = rotate;
1099 set_blitting_type(vc, info, NULL);
1100
989 cols = vc->vc_cols; 1101 cols = vc->vc_cols;
990 rows = vc->vc_rows; 1102 rows = vc->vc_rows;
991 new_cols = info->var.xres / vc->vc_font.width; 1103 new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
992 new_rows = info->var.yres / vc->vc_font.height; 1104 new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1105 new_cols /= vc->vc_font.width;
1106 new_rows /= vc->vc_font.height;
993 vc_resize(vc, new_cols, new_rows); 1107 vc_resize(vc, new_cols, new_rows);
994 1108
995 ops = info->fbcon_par;
996 /* 1109 /*
997 * We must always set the mode. The mode of the previous console 1110 * We must always set the mode. The mode of the previous console
998 * driver could be in the same resolution but we are using different 1111 * driver could be in the same resolution but we are using different
@@ -1030,6 +1143,12 @@ static void fbcon_init(struct vc_data *vc, int init)
1030 1143
1031 if (vc == svc && softback_buf) 1144 if (vc == svc && softback_buf)
1032 fbcon_update_softback(vc); 1145 fbcon_update_softback(vc);
1146
1147 if (ops->rotate_font && ops->rotate_font(info, vc, p)) {
1148 ops->rotate = FB_ROTATE_UR;
1149 set_blitting_type(vc, info, p);
1150 }
1151
1033} 1152}
1034 1153
1035static void fbcon_deinit(struct vc_data *vc) 1154static void fbcon_deinit(struct vc_data *vc)
@@ -1066,15 +1185,6 @@ static void fbcon_deinit(struct vc_data *vc)
1066 * restriction is simplicity & efficiency at the moment. 1185 * restriction is simplicity & efficiency at the moment.
1067 */ 1186 */
1068 1187
1069static __inline__ int real_y(struct display *p, int ypos)
1070{
1071 int rows = p->vrows;
1072
1073 ypos += p->yscroll;
1074 return ypos < rows ? ypos : ypos - rows;
1075}
1076
1077
1078static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, 1188static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
1079 int width) 1189 int width)
1080{ 1190{
@@ -1162,13 +1272,6 @@ static int scrollback_phys_max = 0;
1162static int scrollback_max = 0; 1272static int scrollback_max = 0;
1163static int scrollback_current = 0; 1273static int scrollback_current = 0;
1164 1274
1165static int update_var(int con, struct fb_info *info)
1166{
1167 if (con == ((struct fbcon_ops *)info->fbcon_par)->currcon)
1168 return fb_pan_display(info, &info->var);
1169 return 0;
1170}
1171
1172/* 1275/*
1173 * If no vc is existent yet, just set struct display 1276 * If no vc is existent yet, just set struct display
1174 */ 1277 */
@@ -1178,7 +1281,6 @@ static void fbcon_preset_disp(struct fb_info *info, struct fb_var_screeninfo *va
1178 struct display *p = &fb_display[unit]; 1281 struct display *p = &fb_display[unit];
1179 struct display *t = &fb_display[fg_console]; 1282 struct display *t = &fb_display[fg_console];
1180 1283
1181 var->xoffset = var->yoffset = p->yscroll = 0;
1182 if (var_to_display(p, var, info)) 1284 if (var_to_display(p, var, info))
1183 return; 1285 return;
1184 1286
@@ -1194,9 +1296,9 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
1194 struct display *p = &fb_display[vc->vc_num], *t; 1296 struct display *p = &fb_display[vc->vc_num], *t;
1195 struct vc_data **default_mode = vc->vc_display_fg; 1297 struct vc_data **default_mode = vc->vc_display_fg;
1196 struct vc_data *svc = *default_mode; 1298 struct vc_data *svc = *default_mode;
1299 struct fbcon_ops *ops = info->fbcon_par;
1197 int rows, cols, charcnt = 256; 1300 int rows, cols, charcnt = 256;
1198 1301
1199 var->xoffset = var->yoffset = p->yscroll = 0;
1200 if (var_to_display(p, var, info)) 1302 if (var_to_display(p, var, info))
1201 return; 1303 return;
1202 t = &fb_display[svc->vc_num]; 1304 t = &fb_display[svc->vc_num];
@@ -1213,9 +1315,10 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
1213 1315
1214 var->activate = FB_ACTIVATE_NOW; 1316 var->activate = FB_ACTIVATE_NOW;
1215 info->var.activate = var->activate; 1317 info->var.activate = var->activate;
1216 info->var.yoffset = info->var.xoffset = 0; 1318 var->yoffset = info->var.yoffset;
1319 var->xoffset = info->var.xoffset;
1217 fb_set_var(info, var); 1320 fb_set_var(info, var);
1218 1321 ops->var = info->var;
1219 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); 1322 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1220 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; 1323 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1221 if (charcnt == 256) { 1324 if (charcnt == 256) {
@@ -1231,9 +1334,12 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
1231 if (!*vc->vc_uni_pagedir_loc) 1334 if (!*vc->vc_uni_pagedir_loc)
1232 con_copy_unimap(vc, svc); 1335 con_copy_unimap(vc, svc);
1233 1336
1234 cols = var->xres / vc->vc_font.width; 1337 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
1235 rows = var->yres / vc->vc_font.height; 1338 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1339 cols /= vc->vc_font.width;
1340 rows /= vc->vc_font.height;
1236 vc_resize(vc, cols, rows); 1341 vc_resize(vc, cols, rows);
1342
1237 if (CON_IS_VISIBLE(vc)) { 1343 if (CON_IS_VISIBLE(vc)) {
1238 update_screen(vc); 1344 update_screen(vc);
1239 if (softback_buf) 1345 if (softback_buf)
@@ -1244,15 +1350,16 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
1244static __inline__ void ywrap_up(struct vc_data *vc, int count) 1350static __inline__ void ywrap_up(struct vc_data *vc, int count)
1245{ 1351{
1246 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 1352 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1353 struct fbcon_ops *ops = info->fbcon_par;
1247 struct display *p = &fb_display[vc->vc_num]; 1354 struct display *p = &fb_display[vc->vc_num];
1248 1355
1249 p->yscroll += count; 1356 p->yscroll += count;
1250 if (p->yscroll >= p->vrows) /* Deal with wrap */ 1357 if (p->yscroll >= p->vrows) /* Deal with wrap */
1251 p->yscroll -= p->vrows; 1358 p->yscroll -= p->vrows;
1252 info->var.xoffset = 0; 1359 ops->var.xoffset = 0;
1253 info->var.yoffset = p->yscroll * vc->vc_font.height; 1360 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1254 info->var.vmode |= FB_VMODE_YWRAP; 1361 ops->var.vmode |= FB_VMODE_YWRAP;
1255 update_var(vc->vc_num, info); 1362 ops->update_start(info);
1256 scrollback_max += count; 1363 scrollback_max += count;
1257 if (scrollback_max > scrollback_phys_max) 1364 if (scrollback_max > scrollback_phys_max)
1258 scrollback_max = scrollback_phys_max; 1365 scrollback_max = scrollback_phys_max;
@@ -1262,15 +1369,16 @@ static __inline__ void ywrap_up(struct vc_data *vc, int count)
1262static __inline__ void ywrap_down(struct vc_data *vc, int count) 1369static __inline__ void ywrap_down(struct vc_data *vc, int count)
1263{ 1370{
1264 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 1371 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1372 struct fbcon_ops *ops = info->fbcon_par;
1265 struct display *p = &fb_display[vc->vc_num]; 1373 struct display *p = &fb_display[vc->vc_num];
1266 1374
1267 p->yscroll -= count; 1375 p->yscroll -= count;
1268 if (p->yscroll < 0) /* Deal with wrap */ 1376 if (p->yscroll < 0) /* Deal with wrap */
1269 p->yscroll += p->vrows; 1377 p->yscroll += p->vrows;
1270 info->var.xoffset = 0; 1378 ops->var.xoffset = 0;
1271 info->var.yoffset = p->yscroll * vc->vc_font.height; 1379 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1272 info->var.vmode |= FB_VMODE_YWRAP; 1380 ops->var.vmode |= FB_VMODE_YWRAP;
1273 update_var(vc->vc_num, info); 1381 ops->update_start(info);
1274 scrollback_max -= count; 1382 scrollback_max -= count;
1275 if (scrollback_max < 0) 1383 if (scrollback_max < 0)
1276 scrollback_max = 0; 1384 scrollback_max = 0;
@@ -1289,10 +1397,11 @@ static __inline__ void ypan_up(struct vc_data *vc, int count)
1289 0, 0, 0, vc->vc_rows, vc->vc_cols); 1397 0, 0, 0, vc->vc_rows, vc->vc_cols);
1290 p->yscroll -= p->vrows - vc->vc_rows; 1398 p->yscroll -= p->vrows - vc->vc_rows;
1291 } 1399 }
1292 info->var.xoffset = 0; 1400
1293 info->var.yoffset = p->yscroll * vc->vc_font.height; 1401 ops->var.xoffset = 0;
1294 info->var.vmode &= ~FB_VMODE_YWRAP; 1402 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1295 update_var(vc->vc_num, info); 1403 ops->var.vmode &= ~FB_VMODE_YWRAP;
1404 ops->update_start(info);
1296 fbcon_clear_margins(vc, 1); 1405 fbcon_clear_margins(vc, 1);
1297 scrollback_max += count; 1406 scrollback_max += count;
1298 if (scrollback_max > scrollback_phys_max) 1407 if (scrollback_max > scrollback_phys_max)
@@ -1303,6 +1412,7 @@ static __inline__ void ypan_up(struct vc_data *vc, int count)
1303static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count) 1412static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
1304{ 1413{
1305 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 1414 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1415 struct fbcon_ops *ops = info->fbcon_par;
1306 struct display *p = &fb_display[vc->vc_num]; 1416 struct display *p = &fb_display[vc->vc_num];
1307 int redraw = 0; 1417 int redraw = 0;
1308 1418
@@ -1312,12 +1422,13 @@ static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count)
1312 redraw = 1; 1422 redraw = 1;
1313 } 1423 }
1314 1424
1315 info->var.xoffset = 0;
1316 info->var.yoffset = p->yscroll * vc->vc_font.height;
1317 info->var.vmode &= ~FB_VMODE_YWRAP;
1318 if (redraw) 1425 if (redraw)
1319 fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t); 1426 fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t);
1320 update_var(vc->vc_num, info); 1427
1428 ops->var.xoffset = 0;
1429 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1430 ops->var.vmode &= ~FB_VMODE_YWRAP;
1431 ops->update_start(info);
1321 fbcon_clear_margins(vc, 1); 1432 fbcon_clear_margins(vc, 1);
1322 scrollback_max += count; 1433 scrollback_max += count;
1323 if (scrollback_max > scrollback_phys_max) 1434 if (scrollback_max > scrollback_phys_max)
@@ -1337,10 +1448,11 @@ static __inline__ void ypan_down(struct vc_data *vc, int count)
1337 0, vc->vc_rows, vc->vc_cols); 1448 0, vc->vc_rows, vc->vc_cols);
1338 p->yscroll += p->vrows - vc->vc_rows; 1449 p->yscroll += p->vrows - vc->vc_rows;
1339 } 1450 }
1340 info->var.xoffset = 0; 1451
1341 info->var.yoffset = p->yscroll * vc->vc_font.height; 1452 ops->var.xoffset = 0;
1342 info->var.vmode &= ~FB_VMODE_YWRAP; 1453 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1343 update_var(vc->vc_num, info); 1454 ops->var.vmode &= ~FB_VMODE_YWRAP;
1455 ops->update_start(info);
1344 fbcon_clear_margins(vc, 1); 1456 fbcon_clear_margins(vc, 1);
1345 scrollback_max -= count; 1457 scrollback_max -= count;
1346 if (scrollback_max < 0) 1458 if (scrollback_max < 0)
@@ -1351,6 +1463,7 @@ static __inline__ void ypan_down(struct vc_data *vc, int count)
1351static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) 1463static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
1352{ 1464{
1353 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 1465 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
1466 struct fbcon_ops *ops = info->fbcon_par;
1354 struct display *p = &fb_display[vc->vc_num]; 1467 struct display *p = &fb_display[vc->vc_num];
1355 int redraw = 0; 1468 int redraw = 0;
1356 1469
@@ -1359,12 +1472,14 @@ static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count)
1359 p->yscroll += p->vrows - vc->vc_rows; 1472 p->yscroll += p->vrows - vc->vc_rows;
1360 redraw = 1; 1473 redraw = 1;
1361 } 1474 }
1362 info->var.xoffset = 0; 1475
1363 info->var.yoffset = p->yscroll * vc->vc_font.height;
1364 info->var.vmode &= ~FB_VMODE_YWRAP;
1365 if (redraw) 1476 if (redraw)
1366 fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count); 1477 fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count);
1367 update_var(vc->vc_num, info); 1478
1479 ops->var.xoffset = 0;
1480 ops->var.yoffset = p->yscroll * vc->vc_font.height;
1481 ops->var.vmode &= ~FB_VMODE_YWRAP;
1482 ops->update_start(info);
1368 fbcon_clear_margins(vc, 1); 1483 fbcon_clear_margins(vc, 1);
1369 scrollback_max -= count; 1484 scrollback_max -= count;
1370 if (scrollback_max < 0) 1485 if (scrollback_max < 0)
@@ -1838,31 +1953,41 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int s
1838 height, width); 1953 height, width);
1839} 1954}
1840 1955
1841static __inline__ void updatescrollmode(struct display *p, struct fb_info *info, 1956static __inline__ void updatescrollmode(struct display *p,
1957 struct fb_info *info,
1842 struct vc_data *vc) 1958 struct vc_data *vc)
1843{ 1959{
1960 struct fbcon_ops *ops = info->fbcon_par;
1844 int fh = vc->vc_font.height; 1961 int fh = vc->vc_font.height;
1845 int cap = info->flags; 1962 int cap = info->flags;
1846 int good_pan = (cap & FBINFO_HWACCEL_YPAN) 1963 u16 t = 0;
1847 && divides(info->fix.ypanstep, vc->vc_font.height) 1964 int ypan = FBCON_SWAP(ops->rotate, info->fix.ypanstep,
1848 && info->var.yres_virtual > info->var.yres; 1965 info->fix.xpanstep);
1849 int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) 1966 int ywrap = FBCON_SWAP(ops->rotate, info->fix.ywrapstep, t);
1850 && divides(info->fix.ywrapstep, vc->vc_font.height) 1967 int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
1851 && divides(vc->vc_font.height, info->var.yres_virtual); 1968 int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
1969 info->var.xres_virtual);
1970 int good_pan = (cap & FBINFO_HWACCEL_YPAN) &&
1971 divides(ypan, vc->vc_font.height) && vyres > yres;
1972 int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) &&
1973 divides(ywrap, vc->vc_font.height) &&
1974 divides(vc->vc_font.height, vyres);
1852 int reading_fast = cap & FBINFO_READS_FAST; 1975 int reading_fast = cap & FBINFO_READS_FAST;
1853 int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) && !(cap & FBINFO_HWACCEL_DISABLED); 1976 int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) &&
1854 int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) && !(cap & FBINFO_HWACCEL_DISABLED); 1977 !(cap & FBINFO_HWACCEL_DISABLED);
1855 1978 int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) &&
1856 p->vrows = info->var.yres_virtual/fh; 1979 !(cap & FBINFO_HWACCEL_DISABLED);
1857 if (info->var.yres > (fh * (vc->vc_rows + 1))) 1980
1858 p->vrows -= (info->var.yres - (fh * vc->vc_rows)) / fh; 1981 p->vrows = vyres/fh;
1859 if ((info->var.yres % fh) && (info->var.yres_virtual % fh < 1982 if (yres > (fh * (vc->vc_rows + 1)))
1860 info->var.yres % fh)) 1983 p->vrows -= (yres - (fh * vc->vc_rows)) / fh;
1984 if ((yres % fh) && (vyres % fh < yres % fh))
1861 p->vrows--; 1985 p->vrows--;
1862 1986
1863 if (good_wrap || good_pan) { 1987 if (good_wrap || good_pan) {
1864 if (reading_fast || fast_copyarea) 1988 if (reading_fast || fast_copyarea)
1865 p->scrollmode = good_wrap ? SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE; 1989 p->scrollmode = good_wrap ?
1990 SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
1866 else 1991 else
1867 p->scrollmode = good_wrap ? SCROLL_REDRAW : 1992 p->scrollmode = good_wrap ? SCROLL_REDRAW :
1868 SCROLL_PAN_REDRAW; 1993 SCROLL_PAN_REDRAW;
@@ -1878,17 +2003,23 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width,
1878 unsigned int height) 2003 unsigned int height)
1879{ 2004{
1880 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 2005 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2006 struct fbcon_ops *ops = info->fbcon_par;
1881 struct display *p = &fb_display[vc->vc_num]; 2007 struct display *p = &fb_display[vc->vc_num];
1882 struct fb_var_screeninfo var = info->var; 2008 struct fb_var_screeninfo var = info->var;
1883 int x_diff, y_diff; 2009 int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
1884 int fw = vc->vc_font.width; 2010
1885 int fh = vc->vc_font.height; 2011 virt_w = FBCON_SWAP(ops->rotate, width, height);
1886 2012 virt_h = FBCON_SWAP(ops->rotate, height, width);
1887 var.xres = width * fw; 2013 virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width,
1888 var.yres = height * fh; 2014 vc->vc_font.height);
2015 virt_fh = FBCON_SWAP(ops->rotate, vc->vc_font.height,
2016 vc->vc_font.width);
2017 var.xres = virt_w * virt_fw;
2018 var.yres = virt_h * virt_fh;
1889 x_diff = info->var.xres - var.xres; 2019 x_diff = info->var.xres - var.xres;
1890 y_diff = info->var.yres - var.yres; 2020 y_diff = info->var.yres - var.yres;
1891 if (x_diff < 0 || x_diff > fw || (y_diff < 0 || y_diff > fh)) { 2021 if (x_diff < 0 || x_diff > virt_fw ||
2022 y_diff < 0 || y_diff > virt_fh) {
1892 struct fb_videomode *mode; 2023 struct fb_videomode *mode;
1893 2024
1894 DPRINTK("attempting resize %ix%i\n", var.xres, var.yres); 2025 DPRINTK("attempting resize %ix%i\n", var.xres, var.yres);
@@ -1898,7 +2029,7 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width,
1898 display_to_var(&var, p); 2029 display_to_var(&var, p);
1899 fb_videomode_to_var(&var, mode); 2030 fb_videomode_to_var(&var, mode);
1900 2031
1901 if (width > var.xres/fw || height > var.yres/fh) 2032 if (virt_w > var.xres/virt_fw || virt_h > var.yres/virt_fh)
1902 return -EINVAL; 2033 return -EINVAL;
1903 2034
1904 DPRINTK("resize now %ix%i\n", var.xres, var.yres); 2035 DPRINTK("resize now %ix%i\n", var.xres, var.yres);
@@ -1908,6 +2039,7 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width,
1908 fb_set_var(info, &var); 2039 fb_set_var(info, &var);
1909 } 2040 }
1910 var_to_display(p, &info->var, info); 2041 var_to_display(p, &info->var, info);
2042 ops->var = info->var;
1911 } 2043 }
1912 updatescrollmode(p, info, vc); 2044 updatescrollmode(p, info, vc);
1913 return 0; 2045 return 0;
@@ -1916,11 +2048,13 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width,
1916static int fbcon_switch(struct vc_data *vc) 2048static int fbcon_switch(struct vc_data *vc)
1917{ 2049{
1918 struct fb_info *info, *old_info = NULL; 2050 struct fb_info *info, *old_info = NULL;
2051 struct fbcon_ops *ops;
1919 struct display *p = &fb_display[vc->vc_num]; 2052 struct display *p = &fb_display[vc->vc_num];
1920 struct fb_var_screeninfo var; 2053 struct fb_var_screeninfo var;
1921 int i, prev_console; 2054 int i, prev_console;
1922 2055
1923 info = registered_fb[con2fb_map[vc->vc_num]]; 2056 info = registered_fb[con2fb_map[vc->vc_num]];
2057 ops = info->fbcon_par;
1924 2058
1925 if (softback_top) { 2059 if (softback_top) {
1926 if (softback_lines) 2060 if (softback_lines)
@@ -1939,7 +2073,7 @@ static int fbcon_switch(struct vc_data *vc)
1939 logo_shown = FBCON_LOGO_CANSHOW; 2073 logo_shown = FBCON_LOGO_CANSHOW;
1940 } 2074 }
1941 2075
1942 prev_console = ((struct fbcon_ops *)info->fbcon_par)->currcon; 2076 prev_console = ops->currcon;
1943 if (prev_console != -1) 2077 if (prev_console != -1)
1944 old_info = registered_fb[con2fb_map[prev_console]]; 2078 old_info = registered_fb[con2fb_map[prev_console]];
1945 /* 2079 /*
@@ -1952,9 +2086,9 @@ static int fbcon_switch(struct vc_data *vc)
1952 */ 2086 */
1953 for (i = 0; i < FB_MAX; i++) { 2087 for (i = 0; i < FB_MAX; i++) {
1954 if (registered_fb[i] != NULL && registered_fb[i]->fbcon_par) { 2088 if (registered_fb[i] != NULL && registered_fb[i]->fbcon_par) {
1955 struct fbcon_ops *ops = registered_fb[i]->fbcon_par; 2089 struct fbcon_ops *o = registered_fb[i]->fbcon_par;
1956 2090
1957 ops->currcon = vc->vc_num; 2091 o->currcon = vc->vc_num;
1958 } 2092 }
1959 } 2093 }
1960 memset(&var, 0, sizeof(struct fb_var_screeninfo)); 2094 memset(&var, 0, sizeof(struct fb_var_screeninfo));
@@ -1966,8 +2100,11 @@ static int fbcon_switch(struct vc_data *vc)
1966 * in fb_set_var() 2100 * in fb_set_var()
1967 */ 2101 */
1968 info->var.activate = var.activate; 2102 info->var.activate = var.activate;
1969 info->var.yoffset = info->var.xoffset = p->yscroll = 0; 2103 var.yoffset = info->var.yoffset;
2104 var.xoffset = info->var.xoffset;
2105 var.vmode = info->var.vmode;
1970 fb_set_var(info, &var); 2106 fb_set_var(info, &var);
2107 ops->var = info->var;
1971 2108
1972 if (old_info != NULL && old_info != info) { 2109 if (old_info != NULL && old_info != info) {
1973 if (info->fbops->fb_set_par) 2110 if (info->fbops->fb_set_par)
@@ -1977,7 +2114,12 @@ static int fbcon_switch(struct vc_data *vc)
1977 } 2114 }
1978 2115
1979 set_blitting_type(vc, info, p); 2116 set_blitting_type(vc, info, p);
1980 ((struct fbcon_ops *)info->fbcon_par)->cursor_reset = 1; 2117 ops->cursor_reset = 1;
2118
2119 if (ops->rotate_font && ops->rotate_font(info, vc, p)) {
2120 ops->rotate = FB_ROTATE_UR;
2121 set_blitting_type(vc, info, p);
2122 }
1981 2123
1982 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); 2124 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1983 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; 2125 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
@@ -1997,10 +2139,11 @@ static int fbcon_switch(struct vc_data *vc)
1997 scrollback_phys_max = 0; 2139 scrollback_phys_max = 0;
1998 break; 2140 break;
1999 } 2141 }
2142
2000 scrollback_max = 0; 2143 scrollback_max = 0;
2001 scrollback_current = 0; 2144 scrollback_current = 0;
2002 2145 ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
2003 update_var(vc->vc_num, info); 2146 ops->update_start(info);
2004 fbcon_set_palette(vc, color_table); 2147 fbcon_set_palette(vc, color_table);
2005 fbcon_clear_margins(vc, 0); 2148 fbcon_clear_margins(vc, 0);
2006 2149
@@ -2008,7 +2151,7 @@ static int fbcon_switch(struct vc_data *vc)
2008 2151
2009 logo_shown = fg_console; 2152 logo_shown = fg_console;
2010 /* This is protected above by initmem_freed */ 2153 /* This is protected above by initmem_freed */
2011 fb_show_logo(info); 2154 fb_show_logo(info, ops->rotate);
2012 update_region(vc, 2155 update_region(vc,
2013 vc->vc_origin + vc->vc_size_row * vc->vc_top, 2156 vc->vc_origin + vc->vc_size_row * vc->vc_top,
2014 vc->vc_size_row * (vc->vc_bottom - 2157 vc->vc_size_row * (vc->vc_bottom -
@@ -2047,6 +2190,7 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
2047 var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; 2190 var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
2048 fb_set_var(info, &var); 2191 fb_set_var(info, &var);
2049 ops->graphics = 0; 2192 ops->graphics = 0;
2193 ops->var = info->var;
2050 } 2194 }
2051 } 2195 }
2052 2196
@@ -2135,6 +2279,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
2135 const u8 * data, int userfont) 2279 const u8 * data, int userfont)
2136{ 2280{
2137 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; 2281 struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
2282 struct fbcon_ops *ops = info->fbcon_par;
2138 struct display *p = &fb_display[vc->vc_num]; 2283 struct display *p = &fb_display[vc->vc_num];
2139 int resize; 2284 int resize;
2140 int cnt; 2285 int cnt;
@@ -2214,9 +2359,13 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h,
2214 } 2359 }
2215 2360
2216 if (resize) { 2361 if (resize) {
2217 /* reset wrap/pan */ 2362 int cols, rows;
2218 info->var.xoffset = info->var.yoffset = p->yscroll = 0; 2363
2219 vc_resize(vc, info->var.xres / w, info->var.yres / h); 2364 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2365 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2366 cols /= w;
2367 rows /= h;
2368 vc_resize(vc, cols, rows);
2220 if (CON_IS_VISIBLE(vc) && softback_buf) 2369 if (CON_IS_VISIBLE(vc) && softback_buf)
2221 fbcon_update_softback(vc); 2370 fbcon_update_softback(vc);
2222 } else if (CON_IS_VISIBLE(vc) 2371 } else if (CON_IS_VISIBLE(vc)
@@ -2444,6 +2593,7 @@ static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt)
2444static int fbcon_scrolldelta(struct vc_data *vc, int lines) 2593static int fbcon_scrolldelta(struct vc_data *vc, int lines)
2445{ 2594{
2446 struct fb_info *info = registered_fb[con2fb_map[fg_console]]; 2595 struct fb_info *info = registered_fb[con2fb_map[fg_console]];
2596 struct fbcon_ops *ops = info->fbcon_par;
2447 struct display *p = &fb_display[fg_console]; 2597 struct display *p = &fb_display[fg_console];
2448 int offset, limit, scrollback_old; 2598 int offset, limit, scrollback_old;
2449 2599
@@ -2520,9 +2670,11 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines)
2520 offset += limit; 2670 offset += limit;
2521 else if (offset >= limit) 2671 else if (offset >= limit)
2522 offset -= limit; 2672 offset -= limit;
2523 info->var.xoffset = 0; 2673
2524 info->var.yoffset = offset * vc->vc_font.height; 2674 ops->var.xoffset = 0;
2525 update_var(vc->vc_num, info); 2675 ops->var.yoffset = offset * vc->vc_font.height;
2676 ops->update_start(info);
2677
2526 if (!scrollback_current) 2678 if (!scrollback_current)
2527 fbcon_cursor(vc, CM_DRAW); 2679 fbcon_cursor(vc, CM_DRAW);
2528 return 0; 2680 return 0;
@@ -2570,22 +2722,25 @@ static void fbcon_modechanged(struct fb_info *info)
2570 if (!ops || ops->currcon < 0) 2722 if (!ops || ops->currcon < 0)
2571 return; 2723 return;
2572 vc = vc_cons[ops->currcon].d; 2724 vc = vc_cons[ops->currcon].d;
2573 if (vc->vc_mode != KD_TEXT || registered_fb[con2fb_map[ops->currcon]] != info) 2725 if (vc->vc_mode != KD_TEXT ||
2726 registered_fb[con2fb_map[ops->currcon]] != info)
2574 return; 2727 return;
2575 2728
2576 p = &fb_display[vc->vc_num]; 2729 p = &fb_display[vc->vc_num];
2577 2730 set_blitting_type(vc, info, p);
2578 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
2579 2731
2580 if (CON_IS_VISIBLE(vc)) { 2732 if (CON_IS_VISIBLE(vc)) {
2581 var_to_display(p, &info->var, info); 2733 var_to_display(p, &info->var, info);
2582 cols = info->var.xres / vc->vc_font.width; 2734 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2583 rows = info->var.yres / vc->vc_font.height; 2735 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2736 cols /= vc->vc_font.width;
2737 rows /= vc->vc_font.height;
2584 vc_resize(vc, cols, rows); 2738 vc_resize(vc, cols, rows);
2585 updatescrollmode(p, info, vc); 2739 updatescrollmode(p, info, vc);
2586 scrollback_max = 0; 2740 scrollback_max = 0;
2587 scrollback_current = 0; 2741 scrollback_current = 0;
2588 update_var(vc->vc_num, info); 2742 ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
2743 ops->update_start(info);
2589 fbcon_set_palette(vc, color_table); 2744 fbcon_set_palette(vc, color_table);
2590 update_screen(vc); 2745 update_screen(vc);
2591 if (softback_buf) 2746 if (softback_buf)
@@ -2610,18 +2765,20 @@ static void fbcon_set_all_vcs(struct fb_info *info)
2610 continue; 2765 continue;
2611 2766
2612 p = &fb_display[vc->vc_num]; 2767 p = &fb_display[vc->vc_num];
2613 2768 set_blitting_type(vc, info, p);
2614 info->var.xoffset = info->var.yoffset = p->yscroll = 0;
2615 var_to_display(p, &info->var, info); 2769 var_to_display(p, &info->var, info);
2616 cols = info->var.xres / vc->vc_font.width; 2770 cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
2617 rows = info->var.yres / vc->vc_font.height; 2771 rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
2772 cols /= vc->vc_font.width;
2773 rows /= vc->vc_font.height;
2618 vc_resize(vc, cols, rows); 2774 vc_resize(vc, cols, rows);
2619 2775
2620 if (CON_IS_VISIBLE(vc)) { 2776 if (CON_IS_VISIBLE(vc)) {
2621 updatescrollmode(p, info, vc); 2777 updatescrollmode(p, info, vc);
2622 scrollback_max = 0; 2778 scrollback_max = 0;
2623 scrollback_current = 0; 2779 scrollback_current = 0;
2624 update_var(vc->vc_num, info); 2780 ops->var.xoffset = ops->var.yoffset = p->yscroll = 0;
2781 ops->update_start(info);
2625 fbcon_set_palette(vc, color_table); 2782 fbcon_set_palette(vc, color_table);
2626 update_screen(vc); 2783 update_screen(vc);
2627 if (softback_buf) 2784 if (softback_buf)
@@ -2771,6 +2928,14 @@ static int fbcon_event_notify(struct notifier_block *self,
2771 case FB_EVENT_NEW_MODELIST: 2928 case FB_EVENT_NEW_MODELIST:
2772 fbcon_new_modelist(info); 2929 fbcon_new_modelist(info);
2773 break; 2930 break;
2931 case FB_EVENT_SET_CON_ROTATE:
2932 fbcon_rotate(info, *(int *)event->data);
2933 break;
2934 case FB_EVENT_GET_CON_ROTATE:
2935 ret = fbcon_get_rotate(info);
2936 break;
2937 case FB_EVENT_SET_CON_ROTATE_ALL:
2938 fbcon_rotate_all(info, *(int *)event->data);
2774 } 2939 }
2775 2940
2776 return ret; 2941 return ret;
diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h
index b68e0e2c2d16..accfd7bd8e93 100644
--- a/drivers/video/console/fbcon.h
+++ b/drivers/video/console/fbcon.h
@@ -27,15 +27,15 @@
27 */ 27 */
28 28
29struct display { 29struct display {
30 /* Filled in by the frame buffer device */
31 u_short inverse; /* != 0 text black on white as default */
32 /* Filled in by the low-level console driver */ 30 /* Filled in by the low-level console driver */
33 const u_char *fontdata; 31 const u_char *fontdata;
34 int userfont; /* != 0 if fontdata kmalloc()ed */ 32 int userfont; /* != 0 if fontdata kmalloc()ed */
35 u_short scrollmode; /* Scroll Method */ 33 u_short scrollmode; /* Scroll Method */
34 u_short inverse; /* != 0 text black on white as default */
36 short yscroll; /* Hardware scrolling */ 35 short yscroll; /* Hardware scrolling */
37 int vrows; /* number of virtual rows */ 36 int vrows; /* number of virtual rows */
38 int cursor_shape; 37 int cursor_shape;
38 int con_rotate;
39 u32 xres_virtual; 39 u32 xres_virtual;
40 u32 yres_virtual; 40 u32 yres_virtual;
41 u32 height; 41 u32 height;
@@ -52,6 +52,8 @@ struct display {
52 struct fb_videomode *mode; 52 struct fb_videomode *mode;
53}; 53};
54 54
55extern struct display fb_display[];
56
55struct fbcon_ops { 57struct fbcon_ops {
56 void (*bmove)(struct vc_data *vc, struct fb_info *info, int sy, 58 void (*bmove)(struct vc_data *vc, struct fb_info *info, int sy,
57 int sx, int dy, int dx, int height, int width); 59 int sx, int dy, int dx, int height, int width);
@@ -63,8 +65,12 @@ struct fbcon_ops {
63 void (*clear_margins)(struct vc_data *vc, struct fb_info *info, 65 void (*clear_margins)(struct vc_data *vc, struct fb_info *info,
64 int bottom_only); 66 int bottom_only);
65 void (*cursor)(struct vc_data *vc, struct fb_info *info, 67 void (*cursor)(struct vc_data *vc, struct fb_info *info,
66 struct display *p, int mode, int softback_lines, int fg, int bg); 68 struct display *p, int mode, int softback_lines,
67 69 int fg, int bg);
70 int (*update_start)(struct fb_info *info);
71 int (*rotate_font)(struct fb_info *info, struct vc_data *vc,
72 struct display *p);
73 struct fb_var_screeninfo var; /* copy of the current fb_var_screeninfo */
68 struct timer_list cursor_timer; /* Cursor timer */ 74 struct timer_list cursor_timer; /* Cursor timer */
69 struct fb_cursor cursor_state; 75 struct fb_cursor cursor_state;
70 int currcon; /* Current VC. */ 76 int currcon; /* Current VC. */
@@ -73,7 +79,12 @@ struct fbcon_ops {
73 int blank_state; 79 int blank_state;
74 int graphics; 80 int graphics;
75 int flags; 81 int flags;
82 int rotate;
83 int cur_rotate;
76 char *cursor_data; 84 char *cursor_data;
85 u8 *fontbuffer;
86 u8 *fontdata;
87 u32 fd_size;
77}; 88};
78 /* 89 /*
79 * Attribute Decoding 90 * Attribute Decoding
@@ -168,4 +179,47 @@ extern void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info,
168#endif 179#endif
169extern void fbcon_set_bitops(struct fbcon_ops *ops); 180extern void fbcon_set_bitops(struct fbcon_ops *ops);
170extern int soft_cursor(struct fb_info *info, struct fb_cursor *cursor); 181extern int soft_cursor(struct fb_info *info, struct fb_cursor *cursor);
182
183#define FBCON_ATTRIBUTE_UNDERLINE 1
184#define FBCON_ATTRIBUTE_REVERSE 2
185#define FBCON_ATTRIBUTE_BOLD 4
186
187static inline int real_y(struct display *p, int ypos)
188{
189 int rows = p->vrows;
190
191 ypos += p->yscroll;
192 return ypos < rows ? ypos : ypos - rows;
193}
194
195
196static inline int get_attribute(struct fb_info *info, u16 c)
197{
198 int attribute = 0;
199
200 if (fb_get_color_depth(&info->var, &info->fix) == 1) {
201 if (attr_underline(c))
202 attribute |= FBCON_ATTRIBUTE_UNDERLINE;
203 if (attr_reverse(c))
204 attribute |= FBCON_ATTRIBUTE_REVERSE;
205 if (attr_bold(c))
206 attribute |= FBCON_ATTRIBUTE_BOLD;
207 }
208
209 return attribute;
210}
211
212#define FBCON_SWAP(i,r,v) ({ \
213 typeof(r) _r = (r); \
214 typeof(v) _v = (v); \
215 (void) (&_r == &_v); \
216 (i == FB_ROTATE_UR || i == FB_ROTATE_UD) ? _r : _v; })
217
218#ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION
219extern void fbcon_set_rotate(struct fbcon_ops *ops);
220#else
221#define fbcon_set_rotate(x) do {} while(0)
222#endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */
223
171#endif /* _VIDEO_FBCON_H */ 224#endif /* _VIDEO_FBCON_H */
225
diff --git a/drivers/video/console/fbcon_ccw.c b/drivers/video/console/fbcon_ccw.c
new file mode 100644
index 000000000000..680aabab73c5
--- /dev/null
+++ b/drivers/video/console/fbcon_ccw.c
@@ -0,0 +1,428 @@
1/*
2 * linux/drivers/video/console/fbcon_ccw.c -- Software Rotation - 270 degrees
3 *
4 * Copyright (C) 2005 Antonino Daplas <adaplas @pol.net>
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive for
8 * more details.
9 */
10
11#include <linux/config.h>
12#include <linux/module.h>
13#include <linux/string.h>
14#include <linux/fb.h>
15#include <linux/vt_kern.h>
16#include <linux/console.h>
17#include <asm/types.h>
18#include "fbcon.h"
19#include "fbcon_rotate.h"
20
21/*
22 * Rotation 270 degrees
23 */
24
25static inline void ccw_update_attr(u8 *dst, u8 *src, int attribute,
26 struct vc_data *vc)
27{
28 int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2;
29 int width = (vc->vc_font.height + 7) >> 3;
30 int mod = vc->vc_font.height % 8;
31 u8 c, msk = ~(0xff << offset), msk1 = 0;
32
33 if (mod)
34 msk <<= (8 - mod);
35
36 if (offset > mod)
37 set_bit(FBCON_BIT(7), (void *)&msk1);
38
39 for (i = 0; i < vc->vc_font.width; i++) {
40 for (j = 0; j < width; j++) {
41 c = *src;
42
43 if (attribute & FBCON_ATTRIBUTE_UNDERLINE) {
44 if (j == width - 1)
45 c |= msk;
46
47 if (msk1 && j == width - 2)
48 c |= msk1;
49 }
50
51 if (attribute & FBCON_ATTRIBUTE_BOLD && i)
52 *(dst - width) |= c;
53
54 if (attribute & FBCON_ATTRIBUTE_REVERSE)
55 c = ~c;
56 src++;
57 *dst++ = c;
58 }
59 }
60}
61
62
63static 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 display *p = &fb_display[vc->vc_num];
67 struct fb_copyarea area;
68 u32 vyres = GETVYRES(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
80static void ccw_clear(struct vc_data *vc, struct fb_info *info, int sy,
81 int sx, int height, int width)
82{
83 struct display *p = &fb_display[vc->vc_num];
84 struct fb_fillrect region;
85 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
86 u32 vyres = GETVYRES(p->scrollmode, info);
87
88 region.color = attr_bgcol_ec(bgshift,vc);
89 region.dx = sy * vc->vc_font.height;
90 region.dy = vyres - ((sx + width) * vc->vc_font.width);
91 region.height = width * vc->vc_font.width;
92 region.width = height * vc->vc_font.height;
93 region.rop = ROP_COPY;
94
95 info->fbops->fb_fillrect(info, &region);
96}
97
98static inline void ccw_putcs_aligned(struct vc_data *vc, struct fb_info *info,
99 const u16 *s, u32 attr, u32 cnt,
100 u32 d_pitch, u32 s_pitch, u32 cellsize,
101 struct fb_image *image, u8 *buf, u8 *dst)
102{
103 struct fbcon_ops *ops = info->fbcon_par;
104 u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
105 u32 idx = (vc->vc_font.height + 7) >> 3;
106 u8 *src;
107
108 while (cnt--) {
109 src = ops->fontbuffer + (scr_readw(s--) & charmask)*cellsize;
110
111 if (attr) {
112 ccw_update_attr(buf, src, attr, vc);
113 src = buf;
114 }
115
116 if (likely(idx == 1))
117 __fb_pad_aligned_buffer(dst, d_pitch, src, idx,
118 vc->vc_font.width);
119 else
120 fb_pad_aligned_buffer(dst, d_pitch, src, idx,
121 vc->vc_font.width);
122
123 dst += d_pitch * vc->vc_font.width;
124 }
125
126 info->fbops->fb_imageblit(info, image);
127}
128
129static void ccw_putcs(struct vc_data *vc, struct fb_info *info,
130 const unsigned short *s, int count, int yy, int xx,
131 int fg, int bg)
132{
133 struct fb_image image;
134 struct display *p = &fb_display[vc->vc_num];
135 struct fbcon_ops *ops = info->fbcon_par;
136 u32 width = (vc->vc_font.height + 7)/8;
137 u32 cellsize = width * vc->vc_font.width;
138 u32 maxcnt = info->pixmap.size/cellsize;
139 u32 scan_align = info->pixmap.scan_align - 1;
140 u32 buf_align = info->pixmap.buf_align - 1;
141 u32 cnt, pitch, size;
142 u32 attribute = get_attribute(info, scr_readw(s));
143 u8 *dst, *buf = NULL;
144 u32 vyres = GETVYRES(p->scrollmode, info);
145
146 if (!ops->fontbuffer)
147 return;
148
149 image.fg_color = fg;
150 image.bg_color = bg;
151 image.dx = yy * vc->vc_font.height;
152 image.dy = vyres - ((xx + count) * vc->vc_font.width);
153 image.width = vc->vc_font.height;
154 image.depth = 1;
155
156 if (attribute) {
157 buf = kmalloc(cellsize, GFP_KERNEL);
158 if (!buf)
159 return;
160 }
161
162 s += count - 1;
163
164 while (count) {
165 if (count > maxcnt)
166 cnt = maxcnt;
167 else
168 cnt = count;
169
170 image.height = vc->vc_font.width * cnt;
171 pitch = ((image.width + 7) >> 3) + scan_align;
172 pitch &= ~scan_align;
173 size = pitch * image.height + buf_align;
174 size &= ~buf_align;
175 dst = fb_get_buffer_offset(info, &info->pixmap, size);
176 image.data = dst;
177 ccw_putcs_aligned(vc, info, s, attribute, cnt, pitch,
178 width, cellsize, &image, buf, dst);
179 image.dy += image.height;
180 count -= cnt;
181 s -= cnt;
182 }
183
184 /* buf is always NULL except when in monochrome mode, so in this case
185 it's a gain to check buf against NULL even though kfree() handles
186 NULL pointers just fine */
187 if (unlikely(buf))
188 kfree(buf);
189
190}
191
192static void ccw_clear_margins(struct vc_data *vc, struct fb_info *info,
193 int bottom_only)
194{
195 unsigned int cw = vc->vc_font.width;
196 unsigned int ch = vc->vc_font.height;
197 unsigned int rw = info->var.yres - (vc->vc_cols*cw);
198 unsigned int bh = info->var.xres - (vc->vc_rows*ch);
199 unsigned int bs = vc->vc_rows*ch;
200 struct fb_fillrect region;
201 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
202
203 region.color = attr_bgcol_ec(bgshift,vc);
204 region.rop = ROP_COPY;
205
206 if (rw && !bottom_only) {
207 region.dx = 0;
208 region.dy = info->var.yoffset;
209 region.height = rw;
210 region.width = info->var.xres_virtual;
211 info->fbops->fb_fillrect(info, &region);
212 }
213
214 if (bh) {
215 region.dx = info->var.xoffset + bs;
216 region.dy = 0;
217 region.height = info->var.yres_virtual;
218 region.width = bh;
219 info->fbops->fb_fillrect(info, &region);
220 }
221}
222
223static void ccw_cursor(struct vc_data *vc, struct fb_info *info,
224 struct display *p, int mode, int softback_lines,
225 int fg, int bg)
226{
227 struct fb_cursor cursor;
228 struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
229 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
230 int w = (vc->vc_font.height + 7) >> 3, c;
231 int y = real_y(p, vc->vc_y);
232 int attribute, use_sw = (vc->vc_cursor_type & 0x10);
233 int err = 1, dx, dy;
234 char *src;
235 u32 vyres = GETVYRES(p->scrollmode, info);
236
237 if (!ops->fontbuffer)
238 return;
239
240 cursor.set = 0;
241
242 if (softback_lines) {
243 if (y + softback_lines >= vc->vc_rows) {
244 mode = CM_ERASE;
245 ops->cursor_flash = 0;
246 return;
247 } else
248 y += softback_lines;
249 }
250
251 c = scr_readw((u16 *) vc->vc_pos);
252 attribute = get_attribute(info, c);
253 src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width));
254
255 if (ops->cursor_state.image.data != src ||
256 ops->cursor_reset) {
257 ops->cursor_state.image.data = src;
258 cursor.set |= FB_CUR_SETIMAGE;
259 }
260
261 if (attribute) {
262 u8 *dst;
263
264 dst = kmalloc(w * vc->vc_font.width, GFP_ATOMIC);
265 if (!dst)
266 return;
267 kfree(ops->cursor_data);
268 ops->cursor_data = dst;
269 ccw_update_attr(dst, src, attribute, vc);
270 src = dst;
271 }
272
273 if (ops->cursor_state.image.fg_color != fg ||
274 ops->cursor_state.image.bg_color != bg ||
275 ops->cursor_reset) {
276 ops->cursor_state.image.fg_color = fg;
277 ops->cursor_state.image.bg_color = bg;
278 cursor.set |= FB_CUR_SETCMAP;
279 }
280
281 if (ops->cursor_state.image.height != vc->vc_font.width ||
282 ops->cursor_state.image.width != vc->vc_font.height ||
283 ops->cursor_reset) {
284 ops->cursor_state.image.height = vc->vc_font.width;
285 ops->cursor_state.image.width = vc->vc_font.height;
286 cursor.set |= FB_CUR_SETSIZE;
287 }
288
289 dx = y * vc->vc_font.height;
290 dy = vyres - ((vc->vc_x + 1) * vc->vc_font.width);
291
292 if (ops->cursor_state.image.dx != dx ||
293 ops->cursor_state.image.dy != dy ||
294 ops->cursor_reset) {
295 ops->cursor_state.image.dx = dx;
296 ops->cursor_state.image.dy = dy;
297 cursor.set |= FB_CUR_SETPOS;
298 }
299
300 if (ops->cursor_state.hot.x || ops->cursor_state.hot.y ||
301 ops->cursor_reset) {
302 ops->cursor_state.hot.x = cursor.hot.y = 0;
303 cursor.set |= FB_CUR_SETHOT;
304 }
305
306 if (cursor.set & FB_CUR_SETSIZE ||
307 vc->vc_cursor_type != p->cursor_shape ||
308 ops->cursor_state.mask == NULL ||
309 ops->cursor_reset) {
310 char *tmp, *mask = kmalloc(w*vc->vc_font.width, GFP_ATOMIC);
311 int cur_height, size, i = 0;
312 int width = (vc->vc_font.width + 7)/8;
313
314 if (!mask)
315 return;
316
317 tmp = kmalloc(width * vc->vc_font.height, GFP_ATOMIC);
318
319 if (!tmp) {
320 kfree(mask);
321 return;
322 }
323
324 kfree(ops->cursor_state.mask);
325 ops->cursor_state.mask = mask;
326
327 p->cursor_shape = vc->vc_cursor_type;
328 cursor.set |= FB_CUR_SETSHAPE;
329
330 switch (p->cursor_shape & CUR_HWMASK) {
331 case CUR_NONE:
332 cur_height = 0;
333 break;
334 case CUR_UNDERLINE:
335 cur_height = (vc->vc_font.height < 10) ? 1 : 2;
336 break;
337 case CUR_LOWER_THIRD:
338 cur_height = vc->vc_font.height/3;
339 break;
340 case CUR_LOWER_HALF:
341 cur_height = vc->vc_font.height >> 1;
342 break;
343 case CUR_TWO_THIRDS:
344 cur_height = (vc->vc_font.height << 1)/3;
345 break;
346 case CUR_BLOCK:
347 default:
348 cur_height = vc->vc_font.height;
349 break;
350 }
351
352 size = (vc->vc_font.height - cur_height) * width;
353 while (size--)
354 tmp[i++] = 0;
355 size = cur_height * width;
356 while (size--)
357 tmp[i++] = 0xff;
358 memset(mask, 0, w * vc->vc_font.width);
359 rotate_ccw(tmp, mask, vc->vc_font.width, vc->vc_font.height);
360 kfree(tmp);
361 }
362
363 switch (mode) {
364 case CM_ERASE:
365 ops->cursor_state.enable = 0;
366 break;
367 case CM_DRAW:
368 case CM_MOVE:
369 default:
370 ops->cursor_state.enable = (use_sw) ? 0 : 1;
371 break;
372 }
373
374 cursor.image.data = src;
375 cursor.image.fg_color = ops->cursor_state.image.fg_color;
376 cursor.image.bg_color = ops->cursor_state.image.bg_color;
377 cursor.image.dx = ops->cursor_state.image.dx;
378 cursor.image.dy = ops->cursor_state.image.dy;
379 cursor.image.height = ops->cursor_state.image.height;
380 cursor.image.width = ops->cursor_state.image.width;
381 cursor.hot.x = ops->cursor_state.hot.x;
382 cursor.hot.y = ops->cursor_state.hot.y;
383 cursor.mask = ops->cursor_state.mask;
384 cursor.enable = ops->cursor_state.enable;
385 cursor.image.depth = 1;
386 cursor.rop = ROP_XOR;
387
388 if (info->fbops->fb_cursor)
389 err = info->fbops->fb_cursor(info, &cursor);
390
391 if (err)
392 soft_cursor(info, &cursor);
393
394 ops->cursor_reset = 0;
395}
396
397int ccw_update_start(struct fb_info *info)
398{
399 struct fbcon_ops *ops = info->fbcon_par;
400 struct display *p = &fb_display[ops->currcon];
401 u32 yoffset;
402 u32 vyres = GETVYRES(p->scrollmode, info);
403 int err;
404
405 yoffset = (vyres - info->var.yres) - ops->var.xoffset;
406 ops->var.xoffset = ops->var.yoffset;
407 ops->var.yoffset = yoffset;
408 err = fb_pan_display(info, &ops->var);
409 ops->var.xoffset = info->var.xoffset;
410 ops->var.yoffset = info->var.yoffset;
411 ops->var.vmode = info->var.vmode;
412 return err;
413}
414
415void fbcon_rotate_ccw(struct fbcon_ops *ops)
416{
417 ops->bmove = ccw_bmove;
418 ops->clear = ccw_clear;
419 ops->putcs = ccw_putcs;
420 ops->clear_margins = ccw_clear_margins;
421 ops->cursor = ccw_cursor;
422 ops->update_start = ccw_update_start;
423}
424EXPORT_SYMBOL(fbcon_rotate_ccw);
425
426MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
427MODULE_DESCRIPTION("Console Rotation (270 degrees) Support");
428MODULE_LICENSE("GPL");
diff --git a/drivers/video/console/fbcon_cw.c b/drivers/video/console/fbcon_cw.c
new file mode 100644
index 000000000000..6c6f3b6dd175
--- /dev/null
+++ b/drivers/video/console/fbcon_cw.c
@@ -0,0 +1,412 @@
1/*
2 * linux/drivers/video/console/fbcon_ud.c -- Software Rotation - 90 degrees
3 *
4 * Copyright (C) 2005 Antonino Daplas <adaplas @pol.net>
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive for
8 * more details.
9 */
10
11#include <linux/config.h>
12#include <linux/module.h>
13#include <linux/string.h>
14#include <linux/fb.h>
15#include <linux/vt_kern.h>
16#include <linux/console.h>
17#include <asm/types.h>
18#include "fbcon.h"
19#include "fbcon_rotate.h"
20
21/*
22 * Rotation 90 degrees
23 */
24
25static inline void cw_update_attr(u8 *dst, u8 *src, int attribute,
26 struct vc_data *vc)
27{
28 int i, j, offset = (vc->vc_font.height < 10) ? 1 : 2;
29 int width = (vc->vc_font.height + 7) >> 3;
30 u8 c, t = 0, msk = ~(0xff >> offset);
31
32 for (i = 0; i < vc->vc_font.width; i++) {
33 for (j = 0; j < width; j++) {
34 c = *src;
35 if (attribute & FBCON_ATTRIBUTE_UNDERLINE && !j)
36 c |= msk;
37 if (attribute & FBCON_ATTRIBUTE_BOLD && i)
38 c |= *(src-width);
39 if (attribute & FBCON_ATTRIBUTE_REVERSE)
40 c = ~c;
41 src++;
42 *dst++ = c;
43 t = c;
44 }
45 }
46}
47
48
49static void cw_bmove(struct vc_data *vc, struct fb_info *info, int sy,
50 int sx, int dy, int dx, int height, int width)
51{
52 struct display *p = &fb_display[vc->vc_num];
53 struct fb_copyarea area;
54 u32 vxres = GETVXRES(p->scrollmode, info);
55
56 area.sx = vxres - ((sy + height) * vc->vc_font.height);
57 area.sy = sx * vc->vc_font.width;
58 area.dx = vxres - ((dy + height) * vc->vc_font.height);
59 area.dy = dx * vc->vc_font.width;
60 area.width = height * vc->vc_font.height;
61 area.height = width * vc->vc_font.width;
62
63 info->fbops->fb_copyarea(info, &area);
64}
65
66static void cw_clear(struct vc_data *vc, struct fb_info *info, int sy,
67 int sx, int height, int width)
68{
69 struct display *p = &fb_display[vc->vc_num];
70 struct fb_fillrect region;
71 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
72 u32 vxres = GETVXRES(p->scrollmode, info);
73
74 region.color = attr_bgcol_ec(bgshift,vc);
75 region.dx = vxres - ((sy + height) * vc->vc_font.height);
76 region.dy = sx * vc->vc_font.width;
77 region.height = width * vc->vc_font.width;
78 region.width = height * vc->vc_font.height;
79 region.rop = ROP_COPY;
80
81 info->fbops->fb_fillrect(info, &region);
82}
83
84static inline void cw_putcs_aligned(struct vc_data *vc, struct fb_info *info,
85 const u16 *s, u32 attr, u32 cnt,
86 u32 d_pitch, u32 s_pitch, u32 cellsize,
87 struct fb_image *image, u8 *buf, u8 *dst)
88{
89 struct fbcon_ops *ops = info->fbcon_par;
90 u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
91 u32 idx = (vc->vc_font.height + 7) >> 3;
92 u8 *src;
93
94 while (cnt--) {
95 src = ops->fontbuffer + (scr_readw(s++) & charmask)*cellsize;
96
97 if (attr) {
98 cw_update_attr(buf, src, attr, vc);
99 src = buf;
100 }
101
102 if (likely(idx == 1))
103 __fb_pad_aligned_buffer(dst, d_pitch, src, idx,
104 vc->vc_font.width);
105 else
106 fb_pad_aligned_buffer(dst, d_pitch, src, idx,
107 vc->vc_font.width);
108
109 dst += d_pitch * vc->vc_font.width;
110 }
111
112 info->fbops->fb_imageblit(info, image);
113}
114
115static void cw_putcs(struct vc_data *vc, struct fb_info *info,
116 const unsigned short *s, int count, int yy, int xx,
117 int fg, int bg)
118{
119 struct fb_image image;
120 struct display *p = &fb_display[vc->vc_num];
121 struct fbcon_ops *ops = info->fbcon_par;
122 u32 width = (vc->vc_font.height + 7)/8;
123 u32 cellsize = width * vc->vc_font.width;
124 u32 maxcnt = info->pixmap.size/cellsize;
125 u32 scan_align = info->pixmap.scan_align - 1;
126 u32 buf_align = info->pixmap.buf_align - 1;
127 u32 cnt, pitch, size;
128 u32 attribute = get_attribute(info, scr_readw(s));
129 u8 *dst, *buf = NULL;
130 u32 vxres = GETVXRES(p->scrollmode, info);
131
132 if (!ops->fontbuffer)
133 return;
134
135 image.fg_color = fg;
136 image.bg_color = bg;
137 image.dx = vxres - ((yy + 1) * vc->vc_font.height);
138 image.dy = xx * vc->vc_font.width;
139 image.width = vc->vc_font.height;
140 image.depth = 1;
141
142 if (attribute) {
143 buf = kmalloc(cellsize, GFP_KERNEL);
144 if (!buf)
145 return;
146 }
147
148 while (count) {
149 if (count > maxcnt)
150 cnt = maxcnt;
151 else
152 cnt = count;
153
154 image.height = vc->vc_font.width * cnt;
155 pitch = ((image.width + 7) >> 3) + scan_align;
156 pitch &= ~scan_align;
157 size = pitch * image.height + buf_align;
158 size &= ~buf_align;
159 dst = fb_get_buffer_offset(info, &info->pixmap, size);
160 image.data = dst;
161 cw_putcs_aligned(vc, info, s, attribute, cnt, pitch,
162 width, cellsize, &image, buf, dst);
163 image.dy += image.height;
164 count -= cnt;
165 s += cnt;
166 }
167
168 /* buf is always NULL except when in monochrome mode, so in this case
169 it's a gain to check buf against NULL even though kfree() handles
170 NULL pointers just fine */
171 if (unlikely(buf))
172 kfree(buf);
173
174}
175
176static void cw_clear_margins(struct vc_data *vc, struct fb_info *info,
177 int bottom_only)
178{
179 unsigned int cw = vc->vc_font.width;
180 unsigned int ch = vc->vc_font.height;
181 unsigned int rw = info->var.yres - (vc->vc_cols*cw);
182 unsigned int bh = info->var.xres - (vc->vc_rows*ch);
183 unsigned int rs = info->var.yres - rw;
184 struct fb_fillrect region;
185 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
186
187 region.color = attr_bgcol_ec(bgshift,vc);
188 region.rop = ROP_COPY;
189
190 if (rw && !bottom_only) {
191 region.dx = 0;
192 region.dy = info->var.yoffset + rs;
193 region.height = rw;
194 region.width = info->var.xres_virtual;
195 info->fbops->fb_fillrect(info, &region);
196 }
197
198 if (bh) {
199 region.dx = info->var.xoffset;
200 region.dy = info->var.yoffset;
201 region.height = info->var.yres;
202 region.width = bh;
203 info->fbops->fb_fillrect(info, &region);
204 }
205}
206
207static void cw_cursor(struct vc_data *vc, struct fb_info *info,
208 struct display *p, int mode, int softback_lines,
209 int fg, int bg)
210{
211 struct fb_cursor cursor;
212 struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
213 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
214 int w = (vc->vc_font.height + 7) >> 3, c;
215 int y = real_y(p, vc->vc_y);
216 int attribute, use_sw = (vc->vc_cursor_type & 0x10);
217 int err = 1, dx, dy;
218 char *src;
219 u32 vxres = GETVXRES(p->scrollmode, info);
220
221 if (!ops->fontbuffer)
222 return;
223
224 cursor.set = 0;
225
226 if (softback_lines) {
227 if (y + softback_lines >= vc->vc_rows) {
228 mode = CM_ERASE;
229 ops->cursor_flash = 0;
230 return;
231 } else
232 y += softback_lines;
233 }
234
235 c = scr_readw((u16 *) vc->vc_pos);
236 attribute = get_attribute(info, c);
237 src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width));
238
239 if (ops->cursor_state.image.data != src ||
240 ops->cursor_reset) {
241 ops->cursor_state.image.data = src;
242 cursor.set |= FB_CUR_SETIMAGE;
243 }
244
245 if (attribute) {
246 u8 *dst;
247
248 dst = kmalloc(w * vc->vc_font.width, GFP_ATOMIC);
249 if (!dst)
250 return;
251 kfree(ops->cursor_data);
252 ops->cursor_data = dst;
253 cw_update_attr(dst, src, attribute, vc);
254 src = dst;
255 }
256
257 if (ops->cursor_state.image.fg_color != fg ||
258 ops->cursor_state.image.bg_color != bg ||
259 ops->cursor_reset) {
260 ops->cursor_state.image.fg_color = fg;
261 ops->cursor_state.image.bg_color = bg;
262 cursor.set |= FB_CUR_SETCMAP;
263 }
264
265 if (ops->cursor_state.image.height != vc->vc_font.width ||
266 ops->cursor_state.image.width != vc->vc_font.height ||
267 ops->cursor_reset) {
268 ops->cursor_state.image.height = vc->vc_font.width;
269 ops->cursor_state.image.width = vc->vc_font.height;
270 cursor.set |= FB_CUR_SETSIZE;
271 }
272
273 dx = vxres - ((y * vc->vc_font.height) + vc->vc_font.height);
274 dy = vc->vc_x * vc->vc_font.width;
275
276 if (ops->cursor_state.image.dx != dx ||
277 ops->cursor_state.image.dy != dy ||
278 ops->cursor_reset) {
279 ops->cursor_state.image.dx = dx;
280 ops->cursor_state.image.dy = dy;
281 cursor.set |= FB_CUR_SETPOS;
282 }
283
284 if (ops->cursor_state.hot.x || ops->cursor_state.hot.y ||
285 ops->cursor_reset) {
286 ops->cursor_state.hot.x = cursor.hot.y = 0;
287 cursor.set |= FB_CUR_SETHOT;
288 }
289
290 if (cursor.set & FB_CUR_SETSIZE ||
291 vc->vc_cursor_type != p->cursor_shape ||
292 ops->cursor_state.mask == NULL ||
293 ops->cursor_reset) {
294 char *tmp, *mask = kmalloc(w*vc->vc_font.width, GFP_ATOMIC);
295 int cur_height, size, i = 0;
296 int width = (vc->vc_font.width + 7)/8;
297
298 if (!mask)
299 return;
300
301 tmp = kmalloc(width * vc->vc_font.height, GFP_ATOMIC);
302
303 if (!tmp) {
304 kfree(mask);
305 return;
306 }
307
308 kfree(ops->cursor_state.mask);
309 ops->cursor_state.mask = mask;
310
311 p->cursor_shape = vc->vc_cursor_type;
312 cursor.set |= FB_CUR_SETSHAPE;
313
314 switch (p->cursor_shape & CUR_HWMASK) {
315 case CUR_NONE:
316 cur_height = 0;
317 break;
318 case CUR_UNDERLINE:
319 cur_height = (vc->vc_font.height < 10) ? 1 : 2;
320 break;
321 case CUR_LOWER_THIRD:
322 cur_height = vc->vc_font.height/3;
323 break;
324 case CUR_LOWER_HALF:
325 cur_height = vc->vc_font.height >> 1;
326 break;
327 case CUR_TWO_THIRDS:
328 cur_height = (vc->vc_font.height << 1)/3;
329 break;
330 case CUR_BLOCK:
331 default:
332 cur_height = vc->vc_font.height;
333 break;
334 }
335
336 size = (vc->vc_font.height - cur_height) * width;
337 while (size--)
338 tmp[i++] = 0;
339 size = cur_height * width;
340 while (size--)
341 tmp[i++] = 0xff;
342 memset(mask, 0, w * vc->vc_font.width);
343 rotate_cw(tmp, mask, vc->vc_font.width, vc->vc_font.height);
344 kfree(tmp);
345 }
346
347 switch (mode) {
348 case CM_ERASE:
349 ops->cursor_state.enable = 0;
350 break;
351 case CM_DRAW:
352 case CM_MOVE:
353 default:
354 ops->cursor_state.enable = (use_sw) ? 0 : 1;
355 break;
356 }
357
358 cursor.image.data = src;
359 cursor.image.fg_color = ops->cursor_state.image.fg_color;
360 cursor.image.bg_color = ops->cursor_state.image.bg_color;
361 cursor.image.dx = ops->cursor_state.image.dx;
362 cursor.image.dy = ops->cursor_state.image.dy;
363 cursor.image.height = ops->cursor_state.image.height;
364 cursor.image.width = ops->cursor_state.image.width;
365 cursor.hot.x = ops->cursor_state.hot.x;
366 cursor.hot.y = ops->cursor_state.hot.y;
367 cursor.mask = ops->cursor_state.mask;
368 cursor.enable = ops->cursor_state.enable;
369 cursor.image.depth = 1;
370 cursor.rop = ROP_XOR;
371
372 if (info->fbops->fb_cursor)
373 err = info->fbops->fb_cursor(info, &cursor);
374
375 if (err)
376 soft_cursor(info, &cursor);
377
378 ops->cursor_reset = 0;
379}
380
381int cw_update_start(struct fb_info *info)
382{
383 struct fbcon_ops *ops = info->fbcon_par;
384 struct display *p = &fb_display[ops->currcon];
385 u32 vxres = GETVXRES(p->scrollmode, info);
386 u32 xoffset;
387 int err;
388
389 xoffset = vxres - (info->var.xres + ops->var.yoffset);
390 ops->var.yoffset = ops->var.xoffset;
391 ops->var.xoffset = xoffset;
392 err = fb_pan_display(info, &ops->var);
393 ops->var.xoffset = info->var.xoffset;
394 ops->var.yoffset = info->var.yoffset;
395 ops->var.vmode = info->var.vmode;
396 return err;
397}
398
399void fbcon_rotate_cw(struct fbcon_ops *ops)
400{
401 ops->bmove = cw_bmove;
402 ops->clear = cw_clear;
403 ops->putcs = cw_putcs;
404 ops->clear_margins = cw_clear_margins;
405 ops->cursor = cw_cursor;
406 ops->update_start = cw_update_start;
407}
408EXPORT_SYMBOL(fbcon_rotate_cw);
409
410MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
411MODULE_DESCRIPTION("Console Rotation (90 degrees) Support");
412MODULE_LICENSE("GPL");
diff --git a/drivers/video/console/fbcon_rotate.c b/drivers/video/console/fbcon_rotate.c
new file mode 100644
index 000000000000..ec0dd8fe241c
--- /dev/null
+++ b/drivers/video/console/fbcon_rotate.c
@@ -0,0 +1,117 @@
1/*
2 * linux/drivers/video/console/fbcon_rotate.c -- Software Rotation
3 *
4 * Copyright (C) 2005 Antonino Daplas <adaplas @pol.net>
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive for
8 * more details.
9 */
10
11#include <linux/config.h>
12#include <linux/module.h>
13#include <linux/string.h>
14#include <linux/fb.h>
15#include <linux/vt_kern.h>
16#include <linux/console.h>
17#include <asm/types.h>
18#include "fbcon.h"
19#include "fbcon_rotate.h"
20
21static int fbcon_rotate_font(struct fb_info *info, struct vc_data *vc,
22 struct display *p)
23{
24 struct fbcon_ops *ops = info->fbcon_par;
25 int len, err = 0;
26 int s_cellsize, d_cellsize, i;
27 const u8 *src;
28 u8 *dst;
29
30 if (vc->vc_font.data == ops->fontdata &&
31 p->con_rotate == ops->cur_rotate)
32 goto finished;
33
34 src = ops->fontdata = vc->vc_font.data;
35 ops->cur_rotate = p->con_rotate;
36 len = (!p->userfont) ? 256 : FNTCHARCNT(src);
37 s_cellsize = ((vc->vc_font.width + 7)/8) *
38 vc->vc_font.height;
39 d_cellsize = s_cellsize;
40
41 if (ops->rotate == FB_ROTATE_CW ||
42 ops->rotate == FB_ROTATE_CCW)
43 d_cellsize = ((vc->vc_font.height + 7)/8) *
44 vc->vc_font.width;
45
46 if (info->fbops->fb_sync)
47 info->fbops->fb_sync(info);
48
49 if (ops->fd_size < d_cellsize * len) {
50 dst = kmalloc(d_cellsize * len, GFP_KERNEL);
51
52 if (dst == NULL) {
53 err = -ENOMEM;
54 goto finished;
55 }
56
57 ops->fd_size = d_cellsize * len;
58 kfree(ops->fontbuffer);
59 ops->fontbuffer = dst;
60 }
61
62 dst = ops->fontbuffer;
63 memset(dst, 0, ops->fd_size);
64
65 switch (ops->rotate) {
66 case FB_ROTATE_UD:
67 for (i = len; i--; ) {
68 rotate_ud(src, dst, vc->vc_font.width,
69 vc->vc_font.height);
70
71 src += s_cellsize;
72 dst += d_cellsize;
73 }
74 break;
75 case FB_ROTATE_CW:
76 for (i = len; i--; ) {
77 rotate_cw(src, dst, vc->vc_font.width,
78 vc->vc_font.height);
79 src += s_cellsize;
80 dst += d_cellsize;
81 }
82 break;
83 case FB_ROTATE_CCW:
84 for (i = len; i--; ) {
85 rotate_ccw(src, dst, vc->vc_font.width,
86 vc->vc_font.height);
87 src += s_cellsize;
88 dst += d_cellsize;
89 }
90 break;
91 }
92
93finished:
94 return err;
95}
96
97void fbcon_set_rotate(struct fbcon_ops *ops)
98{
99 ops->rotate_font = fbcon_rotate_font;
100
101 switch(ops->rotate) {
102 case FB_ROTATE_CW:
103 fbcon_rotate_cw(ops);
104 break;
105 case FB_ROTATE_UD:
106 fbcon_rotate_ud(ops);
107 break;
108 case FB_ROTATE_CCW:
109 fbcon_rotate_ccw(ops);
110 break;
111 }
112}
113EXPORT_SYMBOL(fbcon_set_rotate);
114
115MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
116MODULE_DESCRIPTION("Console Rotation Support");
117MODULE_LICENSE("GPL");
diff --git a/drivers/video/console/fbcon_rotate.h b/drivers/video/console/fbcon_rotate.h
new file mode 100644
index 000000000000..90c672096c2e
--- /dev/null
+++ b/drivers/video/console/fbcon_rotate.h
@@ -0,0 +1,105 @@
1/*
2 * linux/drivers/video/console/fbcon_rotate.h -- Software Display Rotation
3 *
4 * Copyright (C) 2005 Antonino Daplas <adaplas@pol.net>
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive
8 * for more details.
9 */
10
11#ifndef _FBCON_ROTATE_H
12#define _FBCON_ROTATE_H
13
14#define FNTCHARCNT(fd) (((int *)(fd))[-3])
15
16#define GETVYRES(s,i) ({ \
17 (s == SCROLL_REDRAW || s == SCROLL_MOVE) ? \
18 (i)->var.yres : (i)->var.yres_virtual; })
19
20#define GETVXRES(s,i) ({ \
21 (s == SCROLL_REDRAW || s == SCROLL_MOVE || !(i)->fix.xpanstep) ? \
22 (i)->var.xres : (i)->var.xres_virtual; })
23
24/*
25 * The bitmap is always big endian
26 */
27#if defined(__LITTLE_ENDIAN)
28#define FBCON_BIT(b) (7 - (b))
29#else
30#define FBCON_BIT(b) (b)
31#endif
32
33static inline int pattern_test_bit(u32 x, u32 y, u32 pitch, const char *pat)
34{
35 u32 tmp = (y * pitch) + x, index = tmp / 8, bit = tmp % 8;
36
37 pat +=index;
38 return (test_bit(FBCON_BIT(bit), (void *)pat));
39}
40
41static inline void pattern_set_bit(u32 x, u32 y, u32 pitch, char *pat)
42{
43 u32 tmp = (y * pitch) + x, index = tmp / 8, bit = tmp % 8;
44
45 pat += index;
46 set_bit(FBCON_BIT(bit), (void *)pat);
47}
48
49static inline void rotate_ud(const char *in, char *out, u32 width, u32 height)
50{
51 int i, j;
52 int shift = width % 8;
53
54 width = (width + 7) & ~7;
55
56 for (i = 0; i < height; i++) {
57 for (j = 0; j < width; j++) {
58 if (pattern_test_bit(j, i, width, in))
59 pattern_set_bit(width - (1 + j + shift),
60 height - (1 + i),
61 width, out);
62 }
63
64 }
65}
66
67static inline void rotate_cw(const char *in, char *out, u32 width, u32 height)
68{
69 int i, j, h = height, w = width;
70 int shift = (8 - (height % 8)) & 7;
71
72 width = (width + 7) & ~7;
73 height = (height + 7) & ~7;
74
75 for (i = 0; i < h; i++) {
76 for (j = 0; j < w; j++) {
77 if (pattern_test_bit(j, i, width, in))
78 pattern_set_bit(height - 1 - i - shift, j,
79 height, out);
80
81 }
82 }
83}
84
85static inline void rotate_ccw(const char *in, char *out, u32 width, u32 height)
86{
87 int i, j, h = height, w = width;
88 int shift = width % 8;
89
90 width = (width + 7) & ~7;
91 height = (height + 7) & ~7;
92
93 for (i = 0; i < h; i++) {
94 for (j = 0; j < w; j++) {
95 if (pattern_test_bit(j, i, width, in))
96 pattern_set_bit(i, width - 1 - j - shift,
97 height, out);
98 }
99 }
100}
101
102extern void fbcon_rotate_cw(struct fbcon_ops *ops);
103extern void fbcon_rotate_ud(struct fbcon_ops *ops);
104extern void fbcon_rotate_ccw(struct fbcon_ops *ops);
105#endif
diff --git a/drivers/video/console/fbcon_ud.c b/drivers/video/console/fbcon_ud.c
new file mode 100644
index 000000000000..2e1d9d4249cd
--- /dev/null
+++ b/drivers/video/console/fbcon_ud.c
@@ -0,0 +1,454 @@
1/*
2 * linux/drivers/video/console/fbcon_ud.c -- Software Rotation - 180 degrees
3 *
4 * Copyright (C) 2005 Antonino Daplas <adaplas @pol.net>
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive for
8 * more details.
9 */
10
11#include <linux/config.h>
12#include <linux/module.h>
13#include <linux/string.h>
14#include <linux/fb.h>
15#include <linux/vt_kern.h>
16#include <linux/console.h>
17#include <asm/types.h>
18#include "fbcon.h"
19#include "fbcon_rotate.h"
20
21/*
22 * Rotation 180 degrees
23 */
24
25static inline void ud_update_attr(u8 *dst, u8 *src, int attribute,
26 struct vc_data *vc)
27{
28 int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
29 int width = (vc->vc_font.width + 7) >> 3;
30 unsigned int cellsize = vc->vc_font.height * width;
31 u8 c;
32
33 offset = offset * width;
34
35 for (i = 0; i < cellsize; i++) {
36 c = src[i];
37 if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i < offset)
38 c = 0xff;
39 if (attribute & FBCON_ATTRIBUTE_BOLD)
40 c |= c << 1;
41 if (attribute & FBCON_ATTRIBUTE_REVERSE)
42 c = ~c;
43 dst[i] = c;
44 }
45}
46
47
48static 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 display *p = &fb_display[vc->vc_num];
52 struct fb_copyarea area;
53 u32 vyres = GETVYRES(p->scrollmode, info);
54 u32 vxres = GETVXRES(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
66static void ud_clear(struct vc_data *vc, struct fb_info *info, int sy,
67 int sx, int height, int width)
68{
69 struct display *p = &fb_display[vc->vc_num];
70 struct fb_fillrect region;
71 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
72 u32 vyres = GETVYRES(p->scrollmode, info);
73 u32 vxres = GETVXRES(p->scrollmode, info);
74
75 region.color = attr_bgcol_ec(bgshift,vc);
76 region.dy = vyres - ((sy + height) * vc->vc_font.height);
77 region.dx = vxres - ((sx + width) * vc->vc_font.width);
78 region.width = width * vc->vc_font.width;
79 region.height = height * vc->vc_font.height;
80 region.rop = ROP_COPY;
81
82 info->fbops->fb_fillrect(info, &region);
83}
84
85static inline void ud_putcs_aligned(struct vc_data *vc, struct fb_info *info,
86 const u16 *s, u32 attr, u32 cnt,
87 u32 d_pitch, u32 s_pitch, u32 cellsize,
88 struct fb_image *image, u8 *buf, u8 *dst)
89{
90 struct fbcon_ops *ops = info->fbcon_par;
91 u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
92 u32 idx = vc->vc_font.width >> 3;
93 u8 *src;
94
95 while (cnt--) {
96 src = ops->fontbuffer + (scr_readw(s--) & charmask)*cellsize;
97
98 if (attr) {
99 ud_update_attr(buf, src, attr, vc);
100 src = buf;
101 }
102
103 if (likely(idx == 1))
104 __fb_pad_aligned_buffer(dst, d_pitch, src, idx,
105 image->height);
106 else
107 fb_pad_aligned_buffer(dst, d_pitch, src, idx,
108 image->height);
109
110 dst += s_pitch;
111 }
112
113 info->fbops->fb_imageblit(info, image);
114}
115
116static inline void ud_putcs_unaligned(struct vc_data *vc,
117 struct fb_info *info, const u16 *s,
118 u32 attr, u32 cnt, u32 d_pitch,
119 u32 s_pitch, u32 cellsize,
120 struct fb_image *image, u8 *buf,
121 u8 *dst)
122{
123 struct fbcon_ops *ops = info->fbcon_par;
124 u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
125 u32 shift_low = 0, mod = vc->vc_font.width % 8;
126 u32 shift_high = 8;
127 u32 idx = vc->vc_font.width >> 3;
128 u8 *src;
129
130 while (cnt--) {
131 src = ops->fontbuffer + (scr_readw(s--) & charmask)*cellsize;
132
133 if (attr) {
134 ud_update_attr(buf, src, attr, vc);
135 src = buf;
136 }
137
138 fb_pad_unaligned_buffer(dst, d_pitch, src, idx,
139 image->height, shift_high,
140 shift_low, mod);
141 shift_low += mod;
142 dst += (shift_low >= 8) ? s_pitch : s_pitch - 1;
143 shift_low &= 7;
144 shift_high = 8 - shift_low;
145 }
146
147 info->fbops->fb_imageblit(info, image);
148
149}
150
151static void ud_putcs(struct vc_data *vc, struct fb_info *info,
152 const unsigned short *s, int count, int yy, int xx,
153 int fg, int bg)
154{
155 struct fb_image image;
156 struct display *p = &fb_display[vc->vc_num];
157 struct fbcon_ops *ops = info->fbcon_par;
158 u32 width = (vc->vc_font.width + 7)/8;
159 u32 cellsize = width * vc->vc_font.height;
160 u32 maxcnt = info->pixmap.size/cellsize;
161 u32 scan_align = info->pixmap.scan_align - 1;
162 u32 buf_align = info->pixmap.buf_align - 1;
163 u32 mod = vc->vc_font.width % 8, cnt, pitch, size;
164 u32 attribute = get_attribute(info, scr_readw(s));
165 u8 *dst, *buf = NULL;
166 u32 vyres = GETVYRES(p->scrollmode, info);
167 u32 vxres = GETVXRES(p->scrollmode, info);
168
169 if (!ops->fontbuffer)
170 return;
171
172 image.fg_color = fg;
173 image.bg_color = bg;
174 image.dy = vyres - ((yy * vc->vc_font.height) + vc->vc_font.height);
175 image.dx = vxres - ((xx + count) * vc->vc_font.width);
176 image.height = vc->vc_font.height;
177 image.depth = 1;
178
179 if (attribute) {
180 buf = kmalloc(cellsize, GFP_KERNEL);
181 if (!buf)
182 return;
183 }
184
185 s += count - 1;
186
187 while (count) {
188 if (count > maxcnt)
189 cnt = maxcnt;
190 else
191 cnt = count;
192
193 image.width = vc->vc_font.width * cnt;
194 pitch = ((image.width + 7) >> 3) + scan_align;
195 pitch &= ~scan_align;
196 size = pitch * image.height + buf_align;
197 size &= ~buf_align;
198 dst = fb_get_buffer_offset(info, &info->pixmap, size);
199 image.data = dst;
200
201 if (!mod)
202 ud_putcs_aligned(vc, info, s, attribute, cnt, pitch,
203 width, cellsize, &image, buf, dst);
204 else
205 ud_putcs_unaligned(vc, info, s, attribute, cnt, pitch,
206 width, cellsize, &image,
207 buf, dst);
208
209 image.dx += image.width;
210 count -= cnt;
211 s -= cnt;
212 xx += cnt;
213 }
214
215 /* buf is always NULL except when in monochrome mode, so in this case
216 it's a gain to check buf against NULL even though kfree() handles
217 NULL pointers just fine */
218 if (unlikely(buf))
219 kfree(buf);
220
221}
222
223static void ud_clear_margins(struct vc_data *vc, struct fb_info *info,
224 int bottom_only)
225{
226 unsigned int cw = vc->vc_font.width;
227 unsigned int ch = vc->vc_font.height;
228 unsigned int rw = info->var.xres - (vc->vc_cols*cw);
229 unsigned int bh = info->var.yres - (vc->vc_rows*ch);
230 struct fb_fillrect region;
231 int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
232
233 region.color = attr_bgcol_ec(bgshift,vc);
234 region.rop = ROP_COPY;
235
236 if (rw && !bottom_only) {
237 region.dy = 0;
238 region.dx = info->var.xoffset;
239 region.width = rw;
240 region.height = info->var.yres_virtual;
241 info->fbops->fb_fillrect(info, &region);
242 }
243
244 if (bh) {
245 region.dy = info->var.yoffset;
246 region.dx = info->var.xoffset;
247 region.height = bh;
248 region.width = info->var.xres;
249 info->fbops->fb_fillrect(info, &region);
250 }
251}
252
253static void ud_cursor(struct vc_data *vc, struct fb_info *info,
254 struct display *p, int mode, int softback_lines,
255 int fg, int bg)
256{
257 struct fb_cursor cursor;
258 struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
259 unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
260 int w = (vc->vc_font.width + 7) >> 3, c;
261 int y = real_y(p, vc->vc_y);
262 int attribute, use_sw = (vc->vc_cursor_type & 0x10);
263 int err = 1, dx, dy;
264 char *src;
265 u32 vyres = GETVYRES(p->scrollmode, info);
266 u32 vxres = GETVXRES(p->scrollmode, info);
267
268 if (!ops->fontbuffer)
269 return;
270
271 cursor.set = 0;
272
273 if (softback_lines) {
274 if (y + softback_lines >= vc->vc_rows) {
275 mode = CM_ERASE;
276 ops->cursor_flash = 0;
277 return;
278 } else
279 y += softback_lines;
280 }
281
282 c = scr_readw((u16 *) vc->vc_pos);
283 attribute = get_attribute(info, c);
284 src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.height));
285
286 if (ops->cursor_state.image.data != src ||
287 ops->cursor_reset) {
288 ops->cursor_state.image.data = src;
289 cursor.set |= FB_CUR_SETIMAGE;
290 }
291
292 if (attribute) {
293 u8 *dst;
294
295 dst = kmalloc(w * vc->vc_font.height, GFP_ATOMIC);
296 if (!dst)
297 return;
298 kfree(ops->cursor_data);
299 ops->cursor_data = dst;
300 ud_update_attr(dst, src, attribute, vc);
301 src = dst;
302 }
303
304 if (ops->cursor_state.image.fg_color != fg ||
305 ops->cursor_state.image.bg_color != bg ||
306 ops->cursor_reset) {
307 ops->cursor_state.image.fg_color = fg;
308 ops->cursor_state.image.bg_color = bg;
309 cursor.set |= FB_CUR_SETCMAP;
310 }
311
312 if (ops->cursor_state.image.height != vc->vc_font.height ||
313 ops->cursor_state.image.width != vc->vc_font.width ||
314 ops->cursor_reset) {
315 ops->cursor_state.image.height = vc->vc_font.height;
316 ops->cursor_state.image.width = vc->vc_font.width;
317 cursor.set |= FB_CUR_SETSIZE;
318 }
319
320 dy = vyres - ((y * vc->vc_font.height) + vc->vc_font.height);
321 dx = vxres - ((vc->vc_x * vc->vc_font.width) + vc->vc_font.width);
322
323 if (ops->cursor_state.image.dx != dx ||
324 ops->cursor_state.image.dy != dy ||
325 ops->cursor_reset) {
326 ops->cursor_state.image.dx = dx;
327 ops->cursor_state.image.dy = dy;
328 cursor.set |= FB_CUR_SETPOS;
329 }
330
331 if (ops->cursor_state.hot.x || ops->cursor_state.hot.y ||
332 ops->cursor_reset) {
333 ops->cursor_state.hot.x = cursor.hot.y = 0;
334 cursor.set |= FB_CUR_SETHOT;
335 }
336
337 if (cursor.set & FB_CUR_SETSIZE ||
338 vc->vc_cursor_type != p->cursor_shape ||
339 ops->cursor_state.mask == NULL ||
340 ops->cursor_reset) {
341 char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
342 int cur_height, size, i = 0;
343 u8 msk = 0xff;
344
345 if (!mask)
346 return;
347
348 kfree(ops->cursor_state.mask);
349 ops->cursor_state.mask = mask;
350
351 p->cursor_shape = vc->vc_cursor_type;
352 cursor.set |= FB_CUR_SETSHAPE;
353
354 switch (p->cursor_shape & CUR_HWMASK) {
355 case CUR_NONE:
356 cur_height = 0;
357 break;
358 case CUR_UNDERLINE:
359 cur_height = (vc->vc_font.height < 10) ? 1 : 2;
360 break;
361 case CUR_LOWER_THIRD:
362 cur_height = vc->vc_font.height/3;
363 break;
364 case CUR_LOWER_HALF:
365 cur_height = vc->vc_font.height >> 1;
366 break;
367 case CUR_TWO_THIRDS:
368 cur_height = (vc->vc_font.height << 1)/3;
369 break;
370 case CUR_BLOCK:
371 default:
372 cur_height = vc->vc_font.height;
373 break;
374 }
375
376 size = cur_height * w;
377
378 while (size--)
379 mask[i++] = msk;
380
381 size = (vc->vc_font.height - cur_height) * w;
382
383 while (size--)
384 mask[i++] = ~msk;
385 }
386
387 switch (mode) {
388 case CM_ERASE:
389 ops->cursor_state.enable = 0;
390 break;
391 case CM_DRAW:
392 case CM_MOVE:
393 default:
394 ops->cursor_state.enable = (use_sw) ? 0 : 1;
395 break;
396 }
397
398 cursor.image.data = src;
399 cursor.image.fg_color = ops->cursor_state.image.fg_color;
400 cursor.image.bg_color = ops->cursor_state.image.bg_color;
401 cursor.image.dx = ops->cursor_state.image.dx;
402 cursor.image.dy = ops->cursor_state.image.dy;
403 cursor.image.height = ops->cursor_state.image.height;
404 cursor.image.width = ops->cursor_state.image.width;
405 cursor.hot.x = ops->cursor_state.hot.x;
406 cursor.hot.y = ops->cursor_state.hot.y;
407 cursor.mask = ops->cursor_state.mask;
408 cursor.enable = ops->cursor_state.enable;
409 cursor.image.depth = 1;
410 cursor.rop = ROP_XOR;
411
412 if (info->fbops->fb_cursor)
413 err = info->fbops->fb_cursor(info, &cursor);
414
415 if (err)
416 soft_cursor(info, &cursor);
417
418 ops->cursor_reset = 0;
419}
420
421int ud_update_start(struct fb_info *info)
422{
423 struct fbcon_ops *ops = info->fbcon_par;
424 struct display *p = &fb_display[ops->currcon];
425 u32 xoffset, yoffset;
426 u32 vyres = GETVYRES(p->scrollmode, info);
427 u32 vxres = GETVXRES(p->scrollmode, info);
428 int err;
429
430 xoffset = (vxres - info->var.xres) - ops->var.xoffset;
431 yoffset = (vyres - info->var.yres) - ops->var.yoffset;
432 ops->var.xoffset = xoffset;
433 ops->var.yoffset = yoffset;
434 err = fb_pan_display(info, &ops->var);
435 ops->var.xoffset = info->var.xoffset;
436 ops->var.yoffset = info->var.yoffset;
437 ops->var.vmode = info->var.vmode;
438 return err;
439}
440
441void fbcon_rotate_ud(struct fbcon_ops *ops)
442{
443 ops->bmove = ud_bmove;
444 ops->clear = ud_clear;
445 ops->putcs = ud_putcs;
446 ops->clear_margins = ud_clear_margins;
447 ops->cursor = ud_cursor;
448 ops->update_start = ud_update_start;
449}
450EXPORT_SYMBOL(fbcon_rotate_ud);
451
452MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
453MODULE_DESCRIPTION("Console Rotation (180 degrees) Support");
454MODULE_LICENSE("GPL");
diff --git a/drivers/video/console/tileblit.c b/drivers/video/console/tileblit.c
index 7f76e2c6a4a1..cb25324a5635 100644
--- a/drivers/video/console/tileblit.c
+++ b/drivers/video/console/tileblit.c
@@ -118,6 +118,18 @@ static void tile_cursor(struct vc_data *vc, struct fb_info *info,
118 info->tileops->fb_tilecursor(info, &cursor); 118 info->tileops->fb_tilecursor(info, &cursor);
119} 119}
120 120
121static int tile_update_start(struct fb_info *info)
122{
123 struct fbcon_ops *ops = info->fbcon_par;
124 int err;
125
126 err = fb_pan_display(info, &ops->var);
127 ops->var.xoffset = info->var.xoffset;
128 ops->var.yoffset = info->var.yoffset;
129 ops->var.vmode = info->var.vmode;
130 return err;
131}
132
121void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info, 133void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info,
122 struct display *p, struct fbcon_ops *ops) 134 struct display *p, struct fbcon_ops *ops)
123{ 135{
@@ -128,6 +140,7 @@ void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info,
128 ops->putcs = tile_putcs; 140 ops->putcs = tile_putcs;
129 ops->clear_margins = tile_clear_margins; 141 ops->clear_margins = tile_clear_margins;
130 ops->cursor = tile_cursor; 142 ops->cursor = tile_cursor;
143 ops->update_start = tile_update_start;
131 144
132 if (p) { 145 if (p) {
133 map.width = vc->vc_font.width; 146 map.width = vc->vc_font.width;