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 c &= charmask; 279 280 /* Clamp to font size, same as bit_putcs_aligned() */ 281 if (c >= vc->vc_font.charcount) 282 c = 0; 283 src = vc->vc_font.data + (c * (w * vc->vc_font.height)); 284 285 if (par->cursor_state.image.data != (const char *)src || 286 par->cursor_reset) { 287 par->cursor_state.image.data = src; 288 cursor.set |= FB_CUR_SETIMAGE; 289 } 290 291 if (attribute) { 292 u8 *dst; 293 294 dst = kmalloc_array(w, vc->vc_font.height, GFP_ATOMIC); 295 if (!dst) 296 return; 297 kfree(par->cursor_data); 298 par->cursor_data = dst; 299 update_attr(dst, src, attribute, vc); 300 src = dst; 301 } 302 303 if (par->cursor_state.image.fg_color != fg || 304 par->cursor_state.image.bg_color != bg || 305 par->cursor_reset) { 306 par->cursor_state.image.fg_color = fg; 307 par->cursor_state.image.bg_color = bg; 308 cursor.set |= FB_CUR_SETCMAP; 309 } 310 311 if ((par->cursor_state.image.dx != (vc->vc_font.width * vc->state.x)) || 312 (par->cursor_state.image.dy != (vc->vc_font.height * y)) || 313 par->cursor_reset) { 314 par->cursor_state.image.dx = vc->vc_font.width * vc->state.x; 315 par->cursor_state.image.dy = vc->vc_font.height * y; 316 cursor.set |= FB_CUR_SETPOS; 317 } 318 319 if (par->cursor_state.image.height != vc->vc_font.height || 320 par->cursor_state.image.width != vc->vc_font.width || 321 par->cursor_reset) { 322 par->cursor_state.image.height = vc->vc_font.height; 323 par->cursor_state.image.width = vc->vc_font.width; 324 cursor.set |= FB_CUR_SETSIZE; 325 } 326 327 if (par->cursor_state.hot.x || par->cursor_state.hot.y || 328 par->cursor_reset) { 329 par->cursor_state.hot.x = cursor.hot.y = 0; 330 cursor.set |= FB_CUR_SETHOT; 331 } 332 333 if (cursor.set & FB_CUR_SETSIZE || 334 vc->vc_cursor_type != par->p->cursor_shape || 335 par->cursor_state.mask == NULL || 336 par->cursor_reset) { 337 unsigned char *mask = kmalloc_array(vc->vc_font.height, w, GFP_ATOMIC); 338 339 if (!mask) 340 return; 341 fbcon_fill_cursor_mask(par, vc, mask); 342 343 kfree(par->cursor_state.mask); 344 par->cursor_state.mask = (const char *)mask; 345 346 par->p->cursor_shape = vc->vc_cursor_type; 347 cursor.set |= FB_CUR_SETSHAPE; 348 } 349 350 par->cursor_state.enable = enable && !use_sw; 351 352 cursor.image.data = src; 353 cursor.image.fg_color = par->cursor_state.image.fg_color; 354 cursor.image.bg_color = par->cursor_state.image.bg_color; 355 cursor.image.dx = par->cursor_state.image.dx; 356 cursor.image.dy = par->cursor_state.image.dy; 357 cursor.image.height = par->cursor_state.image.height; 358 cursor.image.width = par->cursor_state.image.width; 359 cursor.hot.x = par->cursor_state.hot.x; 360 cursor.hot.y = par->cursor_state.hot.y; 361 cursor.mask = par->cursor_state.mask; 362 cursor.enable = par->cursor_state.enable; 363 cursor.image.depth = 1; 364 cursor.rop = ROP_XOR; 365 366 if (info->fbops->fb_cursor) 367 err = info->fbops->fb_cursor(info, &cursor); 368 369 if (err) 370 soft_cursor(info, &cursor); 371 372 par->cursor_reset = 0; 373 } 374 375 static int bit_update_start(struct fb_info *info) 376 { 377 struct fbcon_par *par = info->fbcon_par; 378 int err; 379 380 err = fb_pan_display(info, &par->var); 381 par->var.xoffset = info->var.xoffset; 382 par->var.yoffset = info->var.yoffset; 383 par->var.vmode = info->var.vmode; 384 return err; 385 } 386 387 static const struct fbcon_bitops bit_fbcon_bitops = { 388 .bmove = bit_bmove, 389 .clear = bit_clear, 390 .putcs = bit_putcs, 391 .clear_margins = bit_clear_margins, 392 .cursor = bit_cursor, 393 .update_start = bit_update_start, 394 }; 395 396 void fbcon_set_bitops_ur(struct fbcon_par *par) 397 { 398 par->bitops = &bit_fbcon_bitops; 399 } 400