1 /* 2 * Generic Bit Block Transfer for frame buffers located in system RAM with 3 * packed pixels of any depth. 4 * 5 * Based almost entirely from cfbcopyarea.c (which is based almost entirely 6 * on Geert Uytterhoeven's copyarea routine) 7 * 8 * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net> 9 * 10 * This file is subject to the terms and conditions of the GNU General Public 11 * License. See the file COPYING in the main directory of this archive for 12 * more details. 13 * 14 */ 15 #include <linux/module.h> 16 #include <linux/kernel.h> 17 #include <linux/string.h> 18 #include <linux/fb.h> 19 #include <asm/types.h> 20 #include <asm/io.h> 21 #include "fb_draw.h" 22 23 /* 24 * Generic bitwise copy algorithm 25 */ 26 27 static void 28 bitcpy(struct fb_info *p, unsigned long *dst, unsigned dst_idx, 29 const unsigned long *src, unsigned src_idx, int bits, unsigned n) 30 { 31 unsigned long first, last; 32 int const shift = dst_idx-src_idx; 33 int left, right; 34 35 first = FB_SHIFT_HIGH(p, ~0UL, dst_idx); 36 last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits)); 37 38 if (!shift) { 39 /* Same alignment for source and dest */ 40 if (dst_idx+n <= bits) { 41 /* Single word */ 42 if (last) 43 first &= last; 44 *dst = comp(*src, *dst, first); 45 } else { 46 /* Multiple destination words */ 47 /* Leading bits */ 48 if (first != ~0UL) { 49 *dst = comp(*src, *dst, first); 50 dst++; 51 src++; 52 n -= bits - dst_idx; 53 } 54 55 /* Main chunk */ 56 n /= bits; 57 while (n >= 8) { 58 *dst++ = *src++; 59 *dst++ = *src++; 60 *dst++ = *src++; 61 *dst++ = *src++; 62 *dst++ = *src++; 63 *dst++ = *src++; 64 *dst++ = *src++; 65 *dst++ = *src++; 66 n -= 8; 67 } 68 while (n--) 69 *dst++ = *src++; 70 71 /* Trailing bits */ 72 if (last) 73 *dst = comp(*src, *dst, last); 74 } 75 } else { 76 unsigned long d0, d1; 77 int m; 78 79 /* Different alignment for source and dest */ 80 right = shift & (bits - 1); 81 left = -shift & (bits - 1); 82 83 if (dst_idx+n <= bits) { 84 /* Single destination word */ 85 if (last) 86 first &= last; 87 if (shift > 0) { 88 /* Single source word */ 89 *dst = comp(*src << left, *dst, first); 90 } else if (src_idx+n <= bits) { 91 /* Single source word */ 92 *dst = comp(*src >> right, *dst, first); 93 } else { 94 /* 2 source words */ 95 d0 = *src++; 96 d1 = *src; 97 *dst = comp(d0 >> right | d1 << left, *dst, 98 first); 99 } 100 } else { 101 /* Multiple destination words */ 102 /** We must always remember the last value read, 103 because in case SRC and DST overlap bitwise (e.g. 104 when moving just one pixel in 1bpp), we always 105 collect one full long for DST and that might 106 overlap with the current long from SRC. We store 107 this value in 'd0'. */ 108 d0 = *src++; 109 /* Leading bits */ 110 if (shift > 0) { 111 /* Single source word */ 112 *dst = comp(d0 << left, *dst, first); 113 dst++; 114 n -= bits - dst_idx; 115 } else { 116 /* 2 source words */ 117 d1 = *src++; 118 *dst = comp(d0 >> right | d1 << left, *dst, 119 first); 120 d0 = d1; 121 dst++; 122 n -= bits - dst_idx; 123 } 124 125 /* Main chunk */ 126 m = n % bits; 127 n /= bits; 128 while (n >= 4) { 129 d1 = *src++; 130 *dst++ = d0 >> right | d1 << left; 131 d0 = d1; 132 d1 = *src++; 133 *dst++ = d0 >> right | d1 << left; 134 d0 = d1; 135 d1 = *src++; 136 *dst++ = d0 >> right | d1 << left; 137 d0 = d1; 138 d1 = *src++; 139 *dst++ = d0 >> right | d1 << left; 140 d0 = d1; 141 n -= 4; 142 } 143 while (n--) { 144 d1 = *src++; 145 *dst++ = d0 >> right | d1 << left; 146 d0 = d1; 147 } 148 149 /* Trailing bits */ 150 if (m) { 151 if (m <= bits - right) { 152 /* Single source word */ 153 d0 >>= right; 154 } else { 155 /* 2 source words */ 156 d1 = *src; 157 d0 = d0 >> right | d1 << left; 158 } 159 *dst = comp(d0, *dst, last); 160 } 161 } 162 } 163 } 164 165 /* 166 * Generic bitwise copy algorithm, operating backward 167 */ 168 169 static void 170 bitcpy_rev(struct fb_info *p, unsigned long *dst, unsigned dst_idx, 171 const unsigned long *src, unsigned src_idx, unsigned bits, 172 unsigned n) 173 { 174 unsigned long first, last; 175 int shift; 176 177 dst += (dst_idx + n - 1) / bits; 178 src += (src_idx + n - 1) / bits; 179 dst_idx = (dst_idx + n - 1) % bits; 180 src_idx = (src_idx + n - 1) % bits; 181 182 shift = dst_idx-src_idx; 183 184 first = ~FB_SHIFT_HIGH(p, ~0UL, (dst_idx + 1) % bits); 185 last = FB_SHIFT_HIGH(p, ~0UL, (bits + dst_idx + 1 - n) % bits); 186 187 if (!shift) { 188 /* Same alignment for source and dest */ 189 if ((unsigned long)dst_idx+1 >= n) { 190 /* Single word */ 191 if (first) 192 last &= first; 193 *dst = comp(*src, *dst, last); 194 } else { 195 /* Multiple destination words */ 196 197 /* Leading bits */ 198 if (first) { 199 *dst = comp(*src, *dst, first); 200 dst--; 201 src--; 202 n -= dst_idx+1; 203 } 204 205 /* Main chunk */ 206 n /= bits; 207 while (n >= 8) { 208 *dst-- = *src--; 209 *dst-- = *src--; 210 *dst-- = *src--; 211 *dst-- = *src--; 212 *dst-- = *src--; 213 *dst-- = *src--; 214 *dst-- = *src--; 215 *dst-- = *src--; 216 n -= 8; 217 } 218 while (n--) 219 *dst-- = *src--; 220 /* Trailing bits */ 221 if (last != -1UL) 222 *dst = comp(*src, *dst, last); 223 } 224 } else { 225 /* Different alignment for source and dest */ 226 227 int const left = shift & (bits-1); 228 int const right = -shift & (bits-1); 229 230 if ((unsigned long)dst_idx+1 >= n) { 231 /* Single destination word */ 232 if (first) 233 last &= first; 234 if (shift < 0) { 235 /* Single source word */ 236 *dst = comp(*src >> right, *dst, last); 237 } else if (1+(unsigned long)src_idx >= n) { 238 /* Single source word */ 239 *dst = comp(*src << left, *dst, last); 240 } else { 241 /* 2 source words */ 242 *dst = comp(*src << left | *(src-1) >> right, 243 *dst, last); 244 } 245 } else { 246 /* Multiple destination words */ 247 /** We must always remember the last value read, 248 because in case SRC and DST overlap bitwise (e.g. 249 when moving just one pixel in 1bpp), we always 250 collect one full long for DST and that might 251 overlap with the current long from SRC. We store 252 this value in 'd0'. */ 253 unsigned long d0, d1; 254 int m; 255 256 d0 = *src--; 257 /* Leading bits */ 258 if (shift < 0) { 259 /* Single source word */ 260 d1 = d0; 261 d0 >>= right; 262 } else { 263 /* 2 source words */ 264 d1 = *src--; 265 d0 = d0 << left | d1 >> right; 266 } 267 if (!first) 268 *dst = d0; 269 else 270 *dst = comp(d0, *dst, first); 271 d0 = d1; 272 dst--; 273 n -= dst_idx+1; 274 275 /* Main chunk */ 276 m = n % bits; 277 n /= bits; 278 while (n >= 4) { 279 d1 = *src--; 280 *dst-- = d0 << left | d1 >> right; 281 d0 = d1; 282 d1 = *src--; 283 *dst-- = d0 << left | d1 >> right; 284 d0 = d1; 285 d1 = *src--; 286 *dst-- = d0 << left | d1 >> right; 287 d0 = d1; 288 d1 = *src--; 289 *dst-- = d0 << left | d1 >> right; 290 d0 = d1; 291 n -= 4; 292 } 293 while (n--) { 294 d1 = *src--; 295 *dst-- = d0 << left | d1 >> right; 296 d0 = d1; 297 } 298 299 /* Trailing bits */ 300 if (m) { 301 if (m <= bits - left) { 302 /* Single source word */ 303 d0 <<= left; 304 } else { 305 /* 2 source words */ 306 d1 = *src; 307 d0 = d0 << left | d1 >> right; 308 } 309 *dst = comp(d0, *dst, last); 310 } 311 } 312 } 313 } 314 315 void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area) 316 { 317 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy; 318 u32 height = area->height, width = area->width; 319 unsigned int const bits_per_line = p->fix.line_length * 8u; 320 unsigned long *base = NULL; 321 int bits = BITS_PER_LONG, bytes = bits >> 3; 322 unsigned dst_idx = 0, src_idx = 0, rev_copy = 0; 323 324 if (p->state != FBINFO_STATE_RUNNING) 325 return; 326 327 if (!(p->flags & FBINFO_VIRTFB)) 328 fb_warn_once(p, "Framebuffer is not in virtual address space."); 329 330 /* if the beginning of the target area might overlap with the end of 331 the source area, be have to copy the area reverse. */ 332 if ((dy == sy && dx > sx) || (dy > sy)) { 333 dy += height; 334 sy += height; 335 rev_copy = 1; 336 } 337 338 /* split the base of the framebuffer into a long-aligned address and 339 the index of the first bit */ 340 base = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1)); 341 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1)); 342 /* add offset of source and target area */ 343 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel; 344 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel; 345 346 if (p->fbops->fb_sync) 347 p->fbops->fb_sync(p); 348 349 if (rev_copy) { 350 while (height--) { 351 dst_idx -= bits_per_line; 352 src_idx -= bits_per_line; 353 bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits, 354 base + (src_idx / bits), src_idx % bits, bits, 355 width*p->var.bits_per_pixel); 356 } 357 } else { 358 while (height--) { 359 bitcpy(p, base + (dst_idx / bits), dst_idx % bits, 360 base + (src_idx / bits), src_idx % bits, bits, 361 width*p->var.bits_per_pixel); 362 dst_idx += bits_per_line; 363 src_idx += bits_per_line; 364 } 365 } 366 } 367 368 EXPORT_SYMBOL(sys_copyarea); 369 370 MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>"); 371 MODULE_DESCRIPTION("Generic copyarea (sys-to-sys)"); 372 MODULE_LICENSE("GPL"); 373 374