Line data Source code
1 : /*
2 : * linux/drivers/video/console/bitblit.c -- BitBlitting Operation
3 : *
4 : * Originally from the 'accel_*' routines in drivers/video/console/fbcon.c
5 : *
6 : * Copyright (C) 2004 Antonino Daplas <adaplas @pol.net>
7 : *
8 : * This file is subject to the terms and conditions of the GNU General Public
9 : * License. See the file COPYING in the main directory of this archive for
10 : * more details.
11 : */
12 :
13 : #include <linux/module.h>
14 : #include <linux/slab.h>
15 : #include <linux/string.h>
16 : #include <linux/fb.h>
17 : #include <linux/vt_kern.h>
18 : #include <linux/console.h>
19 : #include <asm/types.h>
20 : #include "fbcon.h"
21 :
22 : /*
23 : * Accelerated handlers.
24 : */
25 0 : static void 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 = DIV_ROUND_UP(vc->vc_font.width, 8);
30 0 : unsigned int cellsize = vc->vc_font.height * width;
31 : u8 c;
32 :
33 0 : offset = cellsize - (offset * width);
34 0 : for (i = 0; i < cellsize; i++) {
35 0 : c = src[i];
36 0 : if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i >= offset)
37 : c = 0xff;
38 0 : if (attribute & FBCON_ATTRIBUTE_BOLD)
39 0 : c |= c >> 1;
40 0 : if (attribute & FBCON_ATTRIBUTE_REVERSE)
41 0 : c = ~c;
42 0 : dst[i] = c;
43 : }
44 0 : }
45 :
46 0 : static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy,
47 : int sx, int dy, int dx, int height, int width)
48 : {
49 : struct fb_copyarea area;
50 :
51 0 : area.sx = sx * vc->vc_font.width;
52 0 : area.sy = sy * vc->vc_font.height;
53 0 : area.dx = dx * vc->vc_font.width;
54 0 : area.dy = dy * vc->vc_font.height;
55 0 : area.height = height * vc->vc_font.height;
56 0 : area.width = width * vc->vc_font.width;
57 :
58 0 : info->fbops->fb_copyarea(info, &area);
59 0 : }
60 :
61 0 : static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy,
62 : int sx, int height, int width)
63 : {
64 0 : int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
65 : struct fb_fillrect region;
66 :
67 0 : region.color = attr_bgcol_ec(bgshift, vc, info);
68 0 : region.dx = sx * vc->vc_font.width;
69 0 : region.dy = sy * vc->vc_font.height;
70 0 : region.width = width * vc->vc_font.width;
71 0 : region.height = height * vc->vc_font.height;
72 0 : region.rop = ROP_COPY;
73 :
74 0 : info->fbops->fb_fillrect(info, ®ion);
75 0 : }
76 :
77 0 : static inline void bit_putcs_aligned(struct vc_data *vc, struct fb_info *info,
78 : const u16 *s, u32 attr, u32 cnt,
79 : u32 d_pitch, u32 s_pitch, u32 cellsize,
80 : struct fb_image *image, u8 *buf, u8 *dst)
81 : {
82 320 : u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
83 320 : u32 idx = vc->vc_font.width >> 3;
84 : u8 *src;
85 :
86 29187 : while (cnt--) {
87 28867 : src = vc->vc_font.data + (scr_readw(s++)&
88 : charmask)*cellsize;
89 :
90 28867 : if (attr) {
91 0 : update_attr(buf, src, attr, vc);
92 : src = buf;
93 : }
94 :
95 28867 : if (likely(idx == 1))
96 28867 : __fb_pad_aligned_buffer(dst, d_pitch, src, idx,
97 : image->height);
98 : else
99 0 : fb_pad_aligned_buffer(dst, d_pitch, src, idx,
100 : image->height);
101 :
102 28867 : dst += s_pitch;
103 : }
104 :
105 320 : info->fbops->fb_imageblit(info, image);
106 : }
107 :
108 0 : static inline void bit_putcs_unaligned(struct vc_data *vc,
109 : struct fb_info *info, const u16 *s,
110 : u32 attr, u32 cnt, u32 d_pitch,
111 : u32 s_pitch, u32 cellsize,
112 : struct fb_image *image, u8 *buf,
113 : u8 *dst)
114 : {
115 0 : u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
116 0 : u32 shift_low = 0, mod = vc->vc_font.width % 8;
117 : u32 shift_high = 8;
118 0 : u32 idx = vc->vc_font.width >> 3;
119 : u8 *src;
120 :
121 0 : while (cnt--) {
122 0 : src = vc->vc_font.data + (scr_readw(s++)&
123 : charmask)*cellsize;
124 :
125 0 : if (attr) {
126 0 : update_attr(buf, src, attr, vc);
127 : src = buf;
128 : }
129 :
130 0 : fb_pad_unaligned_buffer(dst, d_pitch, src, idx,
131 : image->height, shift_high,
132 : shift_low, mod);
133 0 : shift_low += mod;
134 0 : dst += (shift_low >= 8) ? s_pitch : s_pitch - 1;
135 0 : shift_low &= 7;
136 0 : shift_high = 8 - shift_low;
137 : }
138 :
139 0 : info->fbops->fb_imageblit(info, image);
140 :
141 : }
142 :
143 320 : static void bit_putcs(struct vc_data *vc, struct fb_info *info,
144 : const unsigned short *s, int count, int yy, int xx,
145 : int fg, int bg)
146 : {
147 : struct fb_image image;
148 320 : u32 width = DIV_ROUND_UP(vc->vc_font.width, 8);
149 320 : u32 cellsize = width * vc->vc_font.height;
150 320 : u32 maxcnt = info->pixmap.size/cellsize;
151 320 : u32 scan_align = info->pixmap.scan_align - 1;
152 320 : u32 buf_align = info->pixmap.buf_align - 1;
153 320 : u32 mod = vc->vc_font.width % 8, cnt, pitch, size;
154 640 : u32 attribute = get_attribute(info, scr_readw(s));
155 : u8 *dst, *buf = NULL;
156 :
157 320 : image.fg_color = fg;
158 320 : image.bg_color = bg;
159 320 : image.dx = xx * vc->vc_font.width;
160 320 : image.dy = yy * vc->vc_font.height;
161 320 : image.height = vc->vc_font.height;
162 320 : image.depth = 1;
163 :
164 320 : if (attribute) {
165 : buf = kmalloc(cellsize, GFP_ATOMIC);
166 0 : if (!buf)
167 0 : return;
168 : }
169 :
170 640 : while (count) {
171 320 : if (count > maxcnt)
172 : cnt = maxcnt;
173 : else
174 : cnt = count;
175 :
176 320 : image.width = vc->vc_font.width * cnt;
177 320 : pitch = DIV_ROUND_UP(image.width, 8) + scan_align;
178 320 : pitch &= ~scan_align;
179 320 : size = pitch * image.height + buf_align;
180 320 : size &= ~buf_align;
181 320 : dst = fb_get_buffer_offset(info, &info->pixmap, size);
182 320 : image.data = dst;
183 :
184 320 : if (!mod)
185 : bit_putcs_aligned(vc, info, s, attribute, cnt, pitch,
186 : width, cellsize, &image, buf, dst);
187 : else
188 : bit_putcs_unaligned(vc, info, s, attribute, cnt,
189 : pitch, width, cellsize, &image,
190 : buf, dst);
191 :
192 320 : image.dx += cnt * vc->vc_font.width;
193 320 : count -= cnt;
194 320 : s += cnt;
195 : }
196 :
197 : /* buf is always NULL except when in monochrome mode, so in this case
198 : it's a gain to check buf against NULL even though kfree() handles
199 : NULL pointers just fine */
200 320 : if (unlikely(buf))
201 0 : kfree(buf);
202 :
203 : }
204 :
205 3 : static void bit_clear_margins(struct vc_data *vc, struct fb_info *info,
206 : int bottom_only)
207 : {
208 3 : unsigned int cw = vc->vc_font.width;
209 3 : unsigned int ch = vc->vc_font.height;
210 3 : unsigned int rw = info->var.xres - (vc->vc_cols*cw);
211 3 : unsigned int bh = info->var.yres - (vc->vc_rows*ch);
212 3 : unsigned int rs = info->var.xres - rw;
213 3 : unsigned int bs = info->var.yres - bh;
214 : struct fb_fillrect region;
215 :
216 3 : region.color = 0;
217 3 : region.rop = ROP_COPY;
218 :
219 3 : if (rw && !bottom_only) {
220 0 : region.dx = info->var.xoffset + rs;
221 0 : region.dy = 0;
222 0 : region.width = rw;
223 0 : region.height = info->var.yres_virtual;
224 0 : info->fbops->fb_fillrect(info, ®ion);
225 : }
226 :
227 3 : if (bh) {
228 3 : region.dx = info->var.xoffset;
229 3 : region.dy = info->var.yoffset + bs;
230 3 : region.width = rs;
231 3 : region.height = bh;
232 3 : info->fbops->fb_fillrect(info, ®ion);
233 : }
234 3 : }
235 :
236 9831 : static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode,
237 : int softback_lines, int fg, int bg)
238 : {
239 : struct fb_cursor cursor;
240 9831 : struct fbcon_ops *ops = info->fbcon_par;
241 9831 : unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
242 9831 : int w = DIV_ROUND_UP(vc->vc_font.width, 8), c;
243 9831 : int y = real_y(ops->p, vc->vc_y);
244 9831 : int attribute, use_sw = (vc->vc_cursor_type & 0x10);
245 : int err = 1;
246 : char *src;
247 :
248 9831 : cursor.set = 0;
249 :
250 9831 : if (softback_lines) {
251 0 : if (y + softback_lines >= vc->vc_rows) {
252 : mode = CM_ERASE;
253 0 : ops->cursor_flash = 0;
254 0 : return;
255 : } else
256 : y += softback_lines;
257 : }
258 :
259 9831 : c = scr_readw((u16 *) vc->vc_pos);
260 : attribute = get_attribute(info, c);
261 9831 : src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
262 :
263 19650 : if (ops->cursor_state.image.data != src ||
264 9819 : ops->cursor_reset) {
265 14 : ops->cursor_state.image.data = src;
266 14 : cursor.set |= FB_CUR_SETIMAGE;
267 : }
268 :
269 9831 : if (attribute) {
270 : u8 *dst;
271 :
272 0 : dst = kmalloc(w * vc->vc_font.height, GFP_ATOMIC);
273 0 : if (!dst)
274 : return;
275 0 : kfree(ops->cursor_data);
276 0 : ops->cursor_data = dst;
277 0 : update_attr(dst, src, attribute, vc);
278 : src = dst;
279 : }
280 :
281 19659 : if (ops->cursor_state.image.fg_color != fg ||
282 19656 : ops->cursor_state.image.bg_color != bg ||
283 9828 : ops->cursor_reset) {
284 5 : ops->cursor_state.image.fg_color = fg;
285 5 : ops->cursor_state.image.bg_color = bg;
286 5 : cursor.set |= FB_CUR_SETCMAP;
287 : }
288 :
289 19487 : if ((ops->cursor_state.image.dx != (vc->vc_font.width * vc->vc_x)) ||
290 19307 : (ops->cursor_state.image.dy != (vc->vc_font.height * y)) ||
291 9651 : ops->cursor_reset) {
292 182 : ops->cursor_state.image.dx = vc->vc_font.width * vc->vc_x;
293 182 : ops->cursor_state.image.dy = vc->vc_font.height * y;
294 182 : cursor.set |= FB_CUR_SETPOS;
295 : }
296 :
297 19661 : if (ops->cursor_state.image.height != vc->vc_font.height ||
298 19660 : ops->cursor_state.image.width != vc->vc_font.width ||
299 9830 : ops->cursor_reset) {
300 3 : ops->cursor_state.image.height = vc->vc_font.height;
301 3 : ops->cursor_state.image.width = vc->vc_font.width;
302 3 : cursor.set |= FB_CUR_SETSIZE;
303 : }
304 :
305 19662 : if (ops->cursor_state.hot.x || ops->cursor_state.hot.y ||
306 9831 : ops->cursor_reset) {
307 2 : ops->cursor_state.hot.x = cursor.hot.y = 0;
308 2 : cursor.set |= FB_CUR_SETHOT;
309 : }
310 :
311 19659 : if (cursor.set & FB_CUR_SETSIZE ||
312 19656 : vc->vc_cursor_type != ops->p->cursor_shape ||
313 19656 : ops->cursor_state.mask == NULL ||
314 9828 : ops->cursor_reset) {
315 3 : char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
316 : int cur_height, size, i = 0;
317 : u8 msk = 0xff;
318 :
319 3 : if (!mask)
320 : return;
321 :
322 3 : kfree(ops->cursor_state.mask);
323 3 : ops->cursor_state.mask = mask;
324 :
325 3 : ops->p->cursor_shape = vc->vc_cursor_type;
326 3 : cursor.set |= FB_CUR_SETSHAPE;
327 :
328 3 : switch (ops->p->cursor_shape & CUR_HWMASK) {
329 : case CUR_NONE:
330 : cur_height = 0;
331 : break;
332 : case CUR_UNDERLINE:
333 3 : cur_height = (vc->vc_font.height < 10) ? 1 : 2;
334 3 : break;
335 : case CUR_LOWER_THIRD:
336 0 : cur_height = vc->vc_font.height/3;
337 0 : break;
338 : case CUR_LOWER_HALF:
339 0 : cur_height = vc->vc_font.height >> 1;
340 0 : break;
341 : case CUR_TWO_THIRDS:
342 0 : cur_height = (vc->vc_font.height << 1)/3;
343 0 : break;
344 : case CUR_BLOCK:
345 : default:
346 0 : cur_height = vc->vc_font.height;
347 0 : break;
348 : }
349 3 : size = (vc->vc_font.height - cur_height) * w;
350 48 : while (size--)
351 42 : mask[i++] = ~msk;
352 3 : size = cur_height * w;
353 12 : while (size--)
354 6 : mask[i++] = msk;
355 : }
356 :
357 9831 : switch (mode) {
358 : case CM_ERASE:
359 4863 : ops->cursor_state.enable = 0;
360 4863 : break;
361 : case CM_DRAW:
362 : case CM_MOVE:
363 : default:
364 4968 : ops->cursor_state.enable = (use_sw) ? 0 : 1;
365 4968 : break;
366 : }
367 :
368 9831 : cursor.image.data = src;
369 9831 : cursor.image.fg_color = ops->cursor_state.image.fg_color;
370 9831 : cursor.image.bg_color = ops->cursor_state.image.bg_color;
371 9831 : cursor.image.dx = ops->cursor_state.image.dx;
372 9831 : cursor.image.dy = ops->cursor_state.image.dy;
373 9831 : cursor.image.height = ops->cursor_state.image.height;
374 9831 : cursor.image.width = ops->cursor_state.image.width;
375 9831 : cursor.hot.x = ops->cursor_state.hot.x;
376 9831 : cursor.hot.y = ops->cursor_state.hot.y;
377 9831 : cursor.mask = ops->cursor_state.mask;
378 9831 : cursor.enable = ops->cursor_state.enable;
379 9831 : cursor.image.depth = 1;
380 9831 : cursor.rop = ROP_XOR;
381 :
382 9831 : if (info->fbops->fb_cursor)
383 0 : err = info->fbops->fb_cursor(info, &cursor);
384 :
385 9831 : if (err)
386 9831 : soft_cursor(info, &cursor);
387 :
388 9831 : ops->cursor_reset = 0;
389 : }
390 :
391 2 : static int bit_update_start(struct fb_info *info)
392 : {
393 2 : struct fbcon_ops *ops = info->fbcon_par;
394 : int err;
395 :
396 2 : err = fb_pan_display(info, &ops->var);
397 2 : ops->var.xoffset = info->var.xoffset;
398 2 : ops->var.yoffset = info->var.yoffset;
399 2 : ops->var.vmode = info->var.vmode;
400 2 : return err;
401 : }
402 :
403 9 : void fbcon_set_bitops(struct fbcon_ops *ops)
404 : {
405 9 : ops->bmove = bit_bmove;
406 9 : ops->clear = bit_clear;
407 9 : ops->putcs = bit_putcs;
408 9 : ops->clear_margins = bit_clear_margins;
409 9 : ops->cursor = bit_cursor;
410 9 : ops->update_start = bit_update_start;
411 9 : ops->rotate_font = NULL;
412 :
413 : if (ops->rotate)
414 : fbcon_set_rotate(ops);
415 9 : }
416 :
417 : EXPORT_SYMBOL(fbcon_set_bitops);
418 :
419 : MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
420 : MODULE_DESCRIPTION("Bit Blitting Operation");
421 : MODULE_LICENSE("GPL");
422 :
|