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