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 | static void 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 = DIV_ROUND_UP(vc->vc_font.width, 8); |
30 | unsigned int cellsize = vc->vc_font.height * width; |
31 | u8 c; |
32 | |
33 | offset = cellsize - (offset * width); |
34 | for (i = 0; i < cellsize; i++) { |
35 | c = src[i]; |
36 | if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i >= offset) |
37 | c = 0xff; |
38 | if (attribute & FBCON_ATTRIBUTE_BOLD) |
39 | c |= c >> 1; |
40 | if (attribute & FBCON_ATTRIBUTE_REVERSE) |
41 | c = ~c; |
42 | dst[i] = c; |
43 | } |
44 | } |
45 | |
46 | static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy, |
47 | int sx, int dy, int dx, int height, int width) |
48 | { |
49 | struct fb_copyarea area; |
50 | |
51 | area.sx = sx * vc->vc_font.width; |
52 | area.sy = sy * vc->vc_font.height; |
53 | area.dx = dx * vc->vc_font.width; |
54 | area.dy = dy * vc->vc_font.height; |
55 | area.height = height * vc->vc_font.height; |
56 | area.width = width * vc->vc_font.width; |
57 | |
58 | info->fbops->fb_copyarea(info, &area); |
59 | } |
60 | |
61 | static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy, |
62 | int sx, int height, int width) |
63 | { |
64 | int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; |
65 | struct fb_fillrect region; |
66 | |
67 | region.color = attr_bgcol_ec(bgshift, vc, info); |
68 | region.dx = sx * vc->vc_font.width; |
69 | region.dy = sy * vc->vc_font.height; |
70 | region.width = width * vc->vc_font.width; |
71 | region.height = height * vc->vc_font.height; |
72 | region.rop = ROP_COPY; |
73 | |
74 | info->fbops->fb_fillrect(info, ®ion); |
75 | } |
76 | |
77 | 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 | u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; |
83 | u32 idx = vc->vc_font.width >> 3; |
84 | u8 *src; |
85 | |
86 | while (cnt--) { |
87 | src = vc->vc_font.data + (scr_readw(s++)& |
88 | charmask)*cellsize; |
89 | |
90 | if (attr) { |
91 | update_attr(dst: buf, src, attribute: attr, vc); |
92 | src = buf; |
93 | } |
94 | |
95 | if (likely(idx == 1)) |
96 | __fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch: idx, |
97 | height: image->height); |
98 | else |
99 | fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch: idx, |
100 | height: image->height); |
101 | |
102 | dst += s_pitch; |
103 | } |
104 | |
105 | info->fbops->fb_imageblit(info, image); |
106 | } |
107 | |
108 | 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 | u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; |
116 | u32 shift_low = 0, mod = vc->vc_font.width % 8; |
117 | u32 shift_high = 8; |
118 | u32 idx = vc->vc_font.width >> 3; |
119 | u8 *src; |
120 | |
121 | while (cnt--) { |
122 | src = vc->vc_font.data + (scr_readw(s++)& |
123 | charmask)*cellsize; |
124 | |
125 | if (attr) { |
126 | update_attr(dst: buf, src, attribute: attr, vc); |
127 | src = buf; |
128 | } |
129 | |
130 | fb_pad_unaligned_buffer(dst, d_pitch, src, idx, |
131 | height: image->height, shift_high, |
132 | shift_low, mod); |
133 | shift_low += mod; |
134 | dst += (shift_low >= 8) ? s_pitch : s_pitch - 1; |
135 | shift_low &= 7; |
136 | shift_high = 8 - shift_low; |
137 | } |
138 | |
139 | info->fbops->fb_imageblit(info, image); |
140 | |
141 | } |
142 | |
143 | 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 | u32 width = DIV_ROUND_UP(vc->vc_font.width, 8); |
149 | u32 cellsize = width * vc->vc_font.height; |
150 | u32 maxcnt = info->pixmap.size/cellsize; |
151 | u32 scan_align = info->pixmap.scan_align - 1; |
152 | u32 buf_align = info->pixmap.buf_align - 1; |
153 | u32 mod = vc->vc_font.width % 8, cnt, pitch, size; |
154 | u32 attribute = get_attribute(info, scr_readw(s)); |
155 | u8 *dst, *buf = NULL; |
156 | |
157 | image.fg_color = fg; |
158 | image.bg_color = bg; |
159 | image.dx = xx * vc->vc_font.width; |
160 | image.dy = yy * vc->vc_font.height; |
161 | image.height = vc->vc_font.height; |
162 | image.depth = 1; |
163 | |
164 | if (attribute) { |
165 | buf = kmalloc(size: cellsize, GFP_ATOMIC); |
166 | if (!buf) |
167 | return; |
168 | } |
169 | |
170 | while (count) { |
171 | if (count > maxcnt) |
172 | cnt = maxcnt; |
173 | else |
174 | cnt = count; |
175 | |
176 | image.width = vc->vc_font.width * cnt; |
177 | pitch = DIV_ROUND_UP(image.width, 8) + scan_align; |
178 | pitch &= ~scan_align; |
179 | size = pitch * image.height + buf_align; |
180 | size &= ~buf_align; |
181 | dst = fb_get_buffer_offset(info, buf: &info->pixmap, size); |
182 | image.data = dst; |
183 | |
184 | if (!mod) |
185 | bit_putcs_aligned(vc, info, s, attr: attribute, cnt, d_pitch: pitch, |
186 | s_pitch: width, cellsize, image: &image, buf, dst); |
187 | else |
188 | bit_putcs_unaligned(vc, info, s, attr: attribute, cnt, |
189 | d_pitch: pitch, s_pitch: width, cellsize, image: &image, |
190 | buf, dst); |
191 | |
192 | image.dx += cnt * vc->vc_font.width; |
193 | count -= cnt; |
194 | 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 | if (unlikely(buf)) |
201 | kfree(objp: buf); |
202 | |
203 | } |
204 | |
205 | static void bit_clear_margins(struct vc_data *vc, struct fb_info *info, |
206 | int color, int bottom_only) |
207 | { |
208 | unsigned int cw = vc->vc_font.width; |
209 | unsigned int ch = vc->vc_font.height; |
210 | unsigned int rw = info->var.xres - (vc->vc_cols*cw); |
211 | unsigned int bh = info->var.yres - (vc->vc_rows*ch); |
212 | unsigned int rs = info->var.xres - rw; |
213 | unsigned int bs = info->var.yres - bh; |
214 | struct fb_fillrect region; |
215 | |
216 | region.color = color; |
217 | region.rop = ROP_COPY; |
218 | |
219 | if ((int) rw > 0 && !bottom_only) { |
220 | region.dx = info->var.xoffset + rs; |
221 | region.dy = 0; |
222 | region.width = rw; |
223 | region.height = info->var.yres_virtual; |
224 | info->fbops->fb_fillrect(info, ®ion); |
225 | } |
226 | |
227 | if ((int) bh > 0) { |
228 | region.dx = info->var.xoffset; |
229 | region.dy = info->var.yoffset + bs; |
230 | region.width = rs; |
231 | region.height = bh; |
232 | info->fbops->fb_fillrect(info, ®ion); |
233 | } |
234 | } |
235 | |
236 | static void bit_cursor(struct vc_data *vc, struct fb_info *info, bool enable, |
237 | int fg, int bg) |
238 | { |
239 | struct fb_cursor cursor; |
240 | struct fbcon_ops *ops = info->fbcon_par; |
241 | unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; |
242 | int w = DIV_ROUND_UP(vc->vc_font.width, 8), c; |
243 | int y = real_y(p: ops->p, ypos: vc->state.y); |
244 | int attribute, use_sw = vc->vc_cursor_type & CUR_SW; |
245 | int err = 1; |
246 | char *src; |
247 | |
248 | cursor.set = 0; |
249 | |
250 | if (!vc->vc_font.data) |
251 | return; |
252 | |
253 | c = scr_readw((u16 *) vc->vc_pos); |
254 | attribute = get_attribute(info, c); |
255 | src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); |
256 | |
257 | if (ops->cursor_state.image.data != src || |
258 | ops->cursor_reset) { |
259 | ops->cursor_state.image.data = src; |
260 | cursor.set |= FB_CUR_SETIMAGE; |
261 | } |
262 | |
263 | if (attribute) { |
264 | u8 *dst; |
265 | |
266 | dst = kmalloc_array(n: w, size: vc->vc_font.height, GFP_ATOMIC); |
267 | if (!dst) |
268 | return; |
269 | kfree(objp: ops->cursor_data); |
270 | ops->cursor_data = dst; |
271 | update_attr(dst, src, attribute, vc); |
272 | src = dst; |
273 | } |
274 | |
275 | if (ops->cursor_state.image.fg_color != fg || |
276 | ops->cursor_state.image.bg_color != bg || |
277 | ops->cursor_reset) { |
278 | ops->cursor_state.image.fg_color = fg; |
279 | ops->cursor_state.image.bg_color = bg; |
280 | cursor.set |= FB_CUR_SETCMAP; |
281 | } |
282 | |
283 | if ((ops->cursor_state.image.dx != (vc->vc_font.width * vc->state.x)) || |
284 | (ops->cursor_state.image.dy != (vc->vc_font.height * y)) || |
285 | ops->cursor_reset) { |
286 | ops->cursor_state.image.dx = vc->vc_font.width * vc->state.x; |
287 | ops->cursor_state.image.dy = vc->vc_font.height * y; |
288 | cursor.set |= FB_CUR_SETPOS; |
289 | } |
290 | |
291 | if (ops->cursor_state.image.height != vc->vc_font.height || |
292 | ops->cursor_state.image.width != vc->vc_font.width || |
293 | ops->cursor_reset) { |
294 | ops->cursor_state.image.height = vc->vc_font.height; |
295 | ops->cursor_state.image.width = vc->vc_font.width; |
296 | cursor.set |= FB_CUR_SETSIZE; |
297 | } |
298 | |
299 | if (ops->cursor_state.hot.x || ops->cursor_state.hot.y || |
300 | ops->cursor_reset) { |
301 | ops->cursor_state.hot.x = cursor.hot.y = 0; |
302 | cursor.set |= FB_CUR_SETHOT; |
303 | } |
304 | |
305 | if (cursor.set & FB_CUR_SETSIZE || |
306 | vc->vc_cursor_type != ops->p->cursor_shape || |
307 | ops->cursor_state.mask == NULL || |
308 | ops->cursor_reset) { |
309 | char *mask = kmalloc_array(n: w, size: vc->vc_font.height, GFP_ATOMIC); |
310 | int cur_height, size, i = 0; |
311 | u8 msk = 0xff; |
312 | |
313 | if (!mask) |
314 | return; |
315 | |
316 | kfree(objp: ops->cursor_state.mask); |
317 | ops->cursor_state.mask = mask; |
318 | |
319 | ops->p->cursor_shape = vc->vc_cursor_type; |
320 | cursor.set |= FB_CUR_SETSHAPE; |
321 | |
322 | switch (CUR_SIZE(ops->p->cursor_shape)) { |
323 | case CUR_NONE: |
324 | cur_height = 0; |
325 | break; |
326 | case CUR_UNDERLINE: |
327 | cur_height = (vc->vc_font.height < 10) ? 1 : 2; |
328 | break; |
329 | case CUR_LOWER_THIRD: |
330 | cur_height = vc->vc_font.height/3; |
331 | break; |
332 | case CUR_LOWER_HALF: |
333 | cur_height = vc->vc_font.height >> 1; |
334 | break; |
335 | case CUR_TWO_THIRDS: |
336 | cur_height = (vc->vc_font.height << 1)/3; |
337 | break; |
338 | case CUR_BLOCK: |
339 | default: |
340 | cur_height = vc->vc_font.height; |
341 | break; |
342 | } |
343 | size = (vc->vc_font.height - cur_height) * w; |
344 | while (size--) |
345 | mask[i++] = ~msk; |
346 | size = cur_height * w; |
347 | while (size--) |
348 | mask[i++] = msk; |
349 | } |
350 | |
351 | ops->cursor_state.enable = enable && !use_sw; |
352 | |
353 | cursor.image.data = src; |
354 | cursor.image.fg_color = ops->cursor_state.image.fg_color; |
355 | cursor.image.bg_color = ops->cursor_state.image.bg_color; |
356 | cursor.image.dx = ops->cursor_state.image.dx; |
357 | cursor.image.dy = ops->cursor_state.image.dy; |
358 | cursor.image.height = ops->cursor_state.image.height; |
359 | cursor.image.width = ops->cursor_state.image.width; |
360 | cursor.hot.x = ops->cursor_state.hot.x; |
361 | cursor.hot.y = ops->cursor_state.hot.y; |
362 | cursor.mask = ops->cursor_state.mask; |
363 | cursor.enable = ops->cursor_state.enable; |
364 | cursor.image.depth = 1; |
365 | cursor.rop = ROP_XOR; |
366 | |
367 | if (info->fbops->fb_cursor) |
368 | err = info->fbops->fb_cursor(info, &cursor); |
369 | |
370 | if (err) |
371 | soft_cursor(info, cursor: &cursor); |
372 | |
373 | ops->cursor_reset = 0; |
374 | } |
375 | |
376 | static int bit_update_start(struct fb_info *info) |
377 | { |
378 | struct fbcon_ops *ops = info->fbcon_par; |
379 | int err; |
380 | |
381 | err = fb_pan_display(info, var: &ops->var); |
382 | ops->var.xoffset = info->var.xoffset; |
383 | ops->var.yoffset = info->var.yoffset; |
384 | ops->var.vmode = info->var.vmode; |
385 | return err; |
386 | } |
387 | |
388 | void fbcon_set_bitops(struct fbcon_ops *ops) |
389 | { |
390 | ops->bmove = bit_bmove; |
391 | ops->clear = bit_clear; |
392 | ops->putcs = bit_putcs; |
393 | ops->clear_margins = bit_clear_margins; |
394 | ops->cursor = bit_cursor; |
395 | ops->update_start = bit_update_start; |
396 | ops->rotate_font = NULL; |
397 | |
398 | if (ops->rotate) |
399 | fbcon_set_rotate(ops); |
400 | } |
401 | |