Branch data Line data Source code
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/module.h>
12 : : #include <linux/slab.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 : :
25 : 0 : static void ud_update_attr(u8 *dst, u8 *src, int attribute,
26 : : struct vc_data *vc)
27 : : {
28 : 0 : int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
29 : 0 : int width = (vc->vc_font.width + 7) >> 3;
30 : 0 : unsigned int cellsize = vc->vc_font.height * width;
31 : : u8 c;
32 : :
33 : 0 : offset = offset * width;
34 : :
35 : 0 : for (i = 0; i < cellsize; i++) {
36 : 0 : c = src[i];
37 : 0 : if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i < offset)
38 : : c = 0xff;
39 : 0 : if (attribute & FBCON_ATTRIBUTE_BOLD)
40 : 0 : c |= c << 1;
41 : 0 : if (attribute & FBCON_ATTRIBUTE_REVERSE)
42 : 0 : c = ~c;
43 : 0 : dst[i] = c;
44 : : }
45 : 0 : }
46 : :
47 : :
48 : 0 : 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 : 0 : struct fbcon_ops *ops = info->fbcon_par;
52 : : struct fb_copyarea area;
53 : 0 : u32 vyres = GETVYRES(ops->p->scrollmode, info);
54 : 0 : u32 vxres = GETVXRES(ops->p->scrollmode, info);
55 : :
56 : 0 : area.sy = vyres - ((sy + height) * vc->vc_font.height);
57 : 0 : area.sx = vxres - ((sx + width) * vc->vc_font.width);
58 : 0 : area.dy = vyres - ((dy + height) * vc->vc_font.height);
59 : 0 : area.dx = vxres - ((dx + width) * vc->vc_font.width);
60 : 0 : area.height = height * vc->vc_font.height;
61 : 0 : area.width = width * vc->vc_font.width;
62 : :
63 : 0 : info->fbops->fb_copyarea(info, &area);
64 : 0 : }
65 : :
66 : 0 : static void ud_clear(struct vc_data *vc, struct fb_info *info, int sy,
67 : : int sx, int height, int width)
68 : : {
69 : 0 : struct fbcon_ops *ops = info->fbcon_par;
70 : : struct fb_fillrect region;
71 : 0 : int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
72 : 0 : u32 vyres = GETVYRES(ops->p->scrollmode, info);
73 : 0 : u32 vxres = GETVXRES(ops->p->scrollmode, info);
74 : :
75 : 0 : region.color = attr_bgcol_ec(bgshift,vc,info);
76 : 0 : region.dy = vyres - ((sy + height) * vc->vc_font.height);
77 : 0 : region.dx = vxres - ((sx + width) * vc->vc_font.width);
78 : 0 : region.width = width * vc->vc_font.width;
79 : 0 : region.height = height * vc->vc_font.height;
80 : 0 : region.rop = ROP_COPY;
81 : :
82 : 0 : info->fbops->fb_fillrect(info, ®ion);
83 : 0 : }
84 : :
85 : 0 : static 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 : 0 : struct fbcon_ops *ops = info->fbcon_par;
91 : 0 : u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
92 : 0 : u32 idx = vc->vc_font.width >> 3;
93 : : u8 *src;
94 : :
95 : 0 : while (cnt--) {
96 : 0 : src = ops->fontbuffer + (scr_readw(s--) & charmask)*cellsize;
97 : :
98 : 0 : if (attr) {
99 : 0 : ud_update_attr(buf, src, attr, vc);
100 : : src = buf;
101 : : }
102 : :
103 : 0 : if (likely(idx == 1))
104 : 0 : __fb_pad_aligned_buffer(dst, d_pitch, src, idx,
105 : : image->height);
106 : : else
107 : 0 : fb_pad_aligned_buffer(dst, d_pitch, src, idx,
108 : : image->height);
109 : :
110 : 0 : dst += s_pitch;
111 : : }
112 : :
113 : 0 : info->fbops->fb_imageblit(info, image);
114 : 0 : }
115 : :
116 : 0 : static 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 : 0 : struct fbcon_ops *ops = info->fbcon_par;
124 : 0 : u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
125 : 0 : u32 shift_low = 0, mod = vc->vc_font.width % 8;
126 : : u32 shift_high = 8;
127 : 0 : u32 idx = vc->vc_font.width >> 3;
128 : : u8 *src;
129 : :
130 : 0 : while (cnt--) {
131 : 0 : src = ops->fontbuffer + (scr_readw(s--) & charmask)*cellsize;
132 : :
133 : 0 : if (attr) {
134 : 0 : ud_update_attr(buf, src, attr, vc);
135 : : src = buf;
136 : : }
137 : :
138 : 0 : fb_pad_unaligned_buffer(dst, d_pitch, src, idx,
139 : : image->height, shift_high,
140 : : shift_low, mod);
141 : 0 : shift_low += mod;
142 : 0 : dst += (shift_low >= 8) ? s_pitch : s_pitch - 1;
143 : 0 : shift_low &= 7;
144 : 0 : shift_high = 8 - shift_low;
145 : : }
146 : :
147 : 0 : info->fbops->fb_imageblit(info, image);
148 : :
149 : 0 : }
150 : :
151 : 0 : static 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 : 0 : struct fbcon_ops *ops = info->fbcon_par;
157 : 0 : u32 width = (vc->vc_font.width + 7)/8;
158 : 0 : u32 cellsize = width * vc->vc_font.height;
159 : 0 : u32 maxcnt = info->pixmap.size/cellsize;
160 : 0 : u32 scan_align = info->pixmap.scan_align - 1;
161 : 0 : u32 buf_align = info->pixmap.buf_align - 1;
162 : 0 : u32 mod = vc->vc_font.width % 8, cnt, pitch, size;
163 : 0 : u32 attribute = get_attribute(info, scr_readw(s));
164 : : u8 *dst, *buf = NULL;
165 : 0 : u32 vyres = GETVYRES(ops->p->scrollmode, info);
166 : 0 : u32 vxres = GETVXRES(ops->p->scrollmode, info);
167 : :
168 : 0 : if (!ops->fontbuffer)
169 : 0 : return;
170 : :
171 : 0 : image.fg_color = fg;
172 : 0 : image.bg_color = bg;
173 : 0 : image.dy = vyres - ((yy * vc->vc_font.height) + vc->vc_font.height);
174 : 0 : image.dx = vxres - ((xx + count) * vc->vc_font.width);
175 : 0 : image.height = vc->vc_font.height;
176 : 0 : image.depth = 1;
177 : :
178 : 0 : if (attribute) {
179 : : buf = kmalloc(cellsize, GFP_KERNEL);
180 : 0 : if (!buf)
181 : : return;
182 : : }
183 : :
184 : 0 : s += count - 1;
185 : :
186 : 0 : while (count) {
187 : 0 : if (count > maxcnt)
188 : : cnt = maxcnt;
189 : : else
190 : : cnt = count;
191 : :
192 : 0 : image.width = vc->vc_font.width * cnt;
193 : 0 : pitch = ((image.width + 7) >> 3) + scan_align;
194 : 0 : pitch &= ~scan_align;
195 : 0 : size = pitch * image.height + buf_align;
196 : 0 : size &= ~buf_align;
197 : 0 : dst = fb_get_buffer_offset(info, &info->pixmap, size);
198 : 0 : image.data = dst;
199 : :
200 : 0 : if (!mod)
201 : 0 : ud_putcs_aligned(vc, info, s, attribute, cnt, pitch,
202 : : width, cellsize, &image, buf, dst);
203 : : else
204 : 0 : ud_putcs_unaligned(vc, info, s, attribute, cnt, pitch,
205 : : width, cellsize, &image,
206 : : buf, dst);
207 : :
208 : 0 : image.dx += image.width;
209 : 0 : count -= cnt;
210 : 0 : s -= cnt;
211 : : xx += cnt;
212 : : }
213 : :
214 : : /* buf is always NULL except when in monochrome mode, so in this case
215 : : it's a gain to check buf against NULL even though kfree() handles
216 : : NULL pointers just fine */
217 : 0 : if (unlikely(buf))
218 : 0 : kfree(buf);
219 : :
220 : : }
221 : :
222 : 0 : static void ud_clear_margins(struct vc_data *vc, struct fb_info *info,
223 : : int color, int bottom_only)
224 : : {
225 : 0 : unsigned int cw = vc->vc_font.width;
226 : 0 : unsigned int ch = vc->vc_font.height;
227 : 0 : unsigned int rw = info->var.xres - (vc->vc_cols*cw);
228 : 0 : unsigned int bh = info->var.yres - (vc->vc_rows*ch);
229 : : struct fb_fillrect region;
230 : :
231 : 0 : region.color = color;
232 : 0 : region.rop = ROP_COPY;
233 : :
234 : 0 : if ((int) rw > 0 && !bottom_only) {
235 : 0 : region.dy = 0;
236 : 0 : region.dx = info->var.xoffset;
237 : 0 : region.width = rw;
238 : 0 : region.height = info->var.yres_virtual;
239 : 0 : info->fbops->fb_fillrect(info, ®ion);
240 : : }
241 : :
242 : 0 : if ((int) bh > 0) {
243 : 0 : region.dy = info->var.yoffset;
244 : 0 : region.dx = info->var.xoffset;
245 : 0 : region.height = bh;
246 : 0 : region.width = info->var.xres;
247 : 0 : info->fbops->fb_fillrect(info, ®ion);
248 : : }
249 : 0 : }
250 : :
251 : 0 : static void ud_cursor(struct vc_data *vc, struct fb_info *info, int mode,
252 : : int softback_lines, int fg, int bg)
253 : : {
254 : : struct fb_cursor cursor;
255 : 0 : struct fbcon_ops *ops = info->fbcon_par;
256 : 0 : unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
257 : 0 : int w = (vc->vc_font.width + 7) >> 3, c;
258 : 0 : int y = real_y(ops->p, vc->vc_y);
259 : 0 : int attribute, use_sw = (vc->vc_cursor_type & 0x10);
260 : : int err = 1, dx, dy;
261 : : char *src;
262 : 0 : u32 vyres = GETVYRES(ops->p->scrollmode, info);
263 : 0 : u32 vxres = GETVXRES(ops->p->scrollmode, info);
264 : :
265 : 0 : if (!ops->fontbuffer)
266 : 0 : return;
267 : :
268 : 0 : cursor.set = 0;
269 : :
270 : 0 : if (softback_lines) {
271 : 0 : if (y + softback_lines >= vc->vc_rows) {
272 : : mode = CM_ERASE;
273 : 0 : ops->cursor_flash = 0;
274 : 0 : return;
275 : : } else
276 : : y += softback_lines;
277 : : }
278 : :
279 : 0 : c = scr_readw((u16 *) vc->vc_pos);
280 : 0 : attribute = get_attribute(info, c);
281 : 0 : src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.height));
282 : :
283 : 0 : if (ops->cursor_state.image.data != src ||
284 : 0 : ops->cursor_reset) {
285 : 0 : ops->cursor_state.image.data = src;
286 : 0 : cursor.set |= FB_CUR_SETIMAGE;
287 : : }
288 : :
289 : 0 : if (attribute) {
290 : : u8 *dst;
291 : :
292 : 0 : dst = kmalloc_array(w, vc->vc_font.height, GFP_ATOMIC);
293 : 0 : if (!dst)
294 : : return;
295 : 0 : kfree(ops->cursor_data);
296 : 0 : ops->cursor_data = dst;
297 : 0 : ud_update_attr(dst, src, attribute, vc);
298 : : src = dst;
299 : : }
300 : :
301 : 0 : if (ops->cursor_state.image.fg_color != fg ||
302 : 0 : ops->cursor_state.image.bg_color != bg ||
303 : 0 : ops->cursor_reset) {
304 : 0 : ops->cursor_state.image.fg_color = fg;
305 : 0 : ops->cursor_state.image.bg_color = bg;
306 : 0 : cursor.set |= FB_CUR_SETCMAP;
307 : : }
308 : :
309 : 0 : if (ops->cursor_state.image.height != vc->vc_font.height ||
310 : 0 : ops->cursor_state.image.width != vc->vc_font.width ||
311 : 0 : ops->cursor_reset) {
312 : 0 : ops->cursor_state.image.height = vc->vc_font.height;
313 : 0 : ops->cursor_state.image.width = vc->vc_font.width;
314 : 0 : cursor.set |= FB_CUR_SETSIZE;
315 : : }
316 : :
317 : 0 : dy = vyres - ((y * vc->vc_font.height) + vc->vc_font.height);
318 : 0 : dx = vxres - ((vc->vc_x * vc->vc_font.width) + vc->vc_font.width);
319 : :
320 : 0 : if (ops->cursor_state.image.dx != dx ||
321 : 0 : ops->cursor_state.image.dy != dy ||
322 : 0 : ops->cursor_reset) {
323 : 0 : ops->cursor_state.image.dx = dx;
324 : 0 : ops->cursor_state.image.dy = dy;
325 : 0 : cursor.set |= FB_CUR_SETPOS;
326 : : }
327 : :
328 : 0 : if (ops->cursor_state.hot.x || ops->cursor_state.hot.y ||
329 : 0 : ops->cursor_reset) {
330 : 0 : ops->cursor_state.hot.x = cursor.hot.y = 0;
331 : 0 : cursor.set |= FB_CUR_SETHOT;
332 : : }
333 : :
334 : 0 : if (cursor.set & FB_CUR_SETSIZE ||
335 : 0 : vc->vc_cursor_type != ops->p->cursor_shape ||
336 : 0 : ops->cursor_state.mask == NULL ||
337 : 0 : ops->cursor_reset) {
338 : 0 : char *mask = kmalloc_array(w, vc->vc_font.height, GFP_ATOMIC);
339 : : int cur_height, size, i = 0;
340 : : u8 msk = 0xff;
341 : :
342 : 0 : if (!mask)
343 : : return;
344 : :
345 : 0 : kfree(ops->cursor_state.mask);
346 : 0 : ops->cursor_state.mask = mask;
347 : :
348 : 0 : ops->p->cursor_shape = vc->vc_cursor_type;
349 : 0 : cursor.set |= FB_CUR_SETSHAPE;
350 : :
351 : 0 : switch (ops->p->cursor_shape & CUR_HWMASK) {
352 : : case CUR_NONE:
353 : : cur_height = 0;
354 : : break;
355 : : case CUR_UNDERLINE:
356 : 0 : cur_height = (vc->vc_font.height < 10) ? 1 : 2;
357 : 0 : break;
358 : : case CUR_LOWER_THIRD:
359 : 0 : cur_height = vc->vc_font.height/3;
360 : 0 : break;
361 : : case CUR_LOWER_HALF:
362 : 0 : cur_height = vc->vc_font.height >> 1;
363 : 0 : break;
364 : : case CUR_TWO_THIRDS:
365 : 0 : cur_height = (vc->vc_font.height << 1)/3;
366 : 0 : break;
367 : : case CUR_BLOCK:
368 : : default:
369 : 0 : cur_height = vc->vc_font.height;
370 : 0 : break;
371 : : }
372 : :
373 : 0 : size = cur_height * w;
374 : :
375 : 0 : while (size--)
376 : 0 : mask[i++] = msk;
377 : :
378 : 0 : size = (vc->vc_font.height - cur_height) * w;
379 : :
380 : 0 : while (size--)
381 : 0 : mask[i++] = ~msk;
382 : : }
383 : :
384 : 0 : switch (mode) {
385 : : case CM_ERASE:
386 : 0 : ops->cursor_state.enable = 0;
387 : 0 : break;
388 : : case CM_DRAW:
389 : : case CM_MOVE:
390 : : default:
391 : 0 : ops->cursor_state.enable = (use_sw) ? 0 : 1;
392 : 0 : break;
393 : : }
394 : :
395 : 0 : cursor.image.data = src;
396 : 0 : cursor.image.fg_color = ops->cursor_state.image.fg_color;
397 : 0 : cursor.image.bg_color = ops->cursor_state.image.bg_color;
398 : 0 : cursor.image.dx = ops->cursor_state.image.dx;
399 : 0 : cursor.image.dy = ops->cursor_state.image.dy;
400 : 0 : cursor.image.height = ops->cursor_state.image.height;
401 : 0 : cursor.image.width = ops->cursor_state.image.width;
402 : 0 : cursor.hot.x = ops->cursor_state.hot.x;
403 : 0 : cursor.hot.y = ops->cursor_state.hot.y;
404 : 0 : cursor.mask = ops->cursor_state.mask;
405 : 0 : cursor.enable = ops->cursor_state.enable;
406 : 0 : cursor.image.depth = 1;
407 : 0 : cursor.rop = ROP_XOR;
408 : :
409 : 0 : if (info->fbops->fb_cursor)
410 : 0 : err = info->fbops->fb_cursor(info, &cursor);
411 : :
412 : 0 : if (err)
413 : 0 : soft_cursor(info, &cursor);
414 : :
415 : 0 : ops->cursor_reset = 0;
416 : : }
417 : :
418 : 0 : static int ud_update_start(struct fb_info *info)
419 : : {
420 : 0 : struct fbcon_ops *ops = info->fbcon_par;
421 : : int xoffset, yoffset;
422 : 0 : u32 vyres = GETVYRES(ops->p->scrollmode, info);
423 : 0 : u32 vxres = GETVXRES(ops->p->scrollmode, info);
424 : : int err;
425 : :
426 : 0 : xoffset = vxres - info->var.xres - ops->var.xoffset;
427 : 0 : yoffset = vyres - info->var.yres - ops->var.yoffset;
428 : 0 : if (yoffset < 0)
429 : 0 : yoffset += vyres;
430 : 0 : ops->var.xoffset = xoffset;
431 : 0 : ops->var.yoffset = yoffset;
432 : 0 : err = fb_pan_display(info, &ops->var);
433 : 0 : ops->var.xoffset = info->var.xoffset;
434 : 0 : ops->var.yoffset = info->var.yoffset;
435 : 0 : ops->var.vmode = info->var.vmode;
436 : 0 : return err;
437 : : }
438 : :
439 : 0 : void fbcon_rotate_ud(struct fbcon_ops *ops)
440 : : {
441 : 0 : ops->bmove = ud_bmove;
442 : 0 : ops->clear = ud_clear;
443 : 0 : ops->putcs = ud_putcs;
444 : 0 : ops->clear_margins = ud_clear_margins;
445 : 0 : ops->cursor = ud_cursor;
446 : 0 : ops->update_start = ud_update_start;
447 : 0 : }
448 : : EXPORT_SYMBOL(fbcon_rotate_ud);
|