1 /* 2 * Generic function for frame buffer with packed pixels of any depth. 3 * 4 * Copyright (C) 1999-2005 James Simmons <jsimmons@www.infradead.org> 5 * 6 * This file is subject to the terms and conditions of the GNU General Public 7 * License. See the file COPYING in the main directory of this archive for 8 * more details. 9 * 10 * NOTES: 11 * 12 * This is for cfb packed pixels. Iplan and such are incorporated in the 13 * drivers that need them. 14 * 15 * FIXME 16 * 17 * Also need to add code to deal with cards endians that are different than 18 * the native cpu endians. I also need to deal with MSB position in the word. 19 * 20 * The two functions or copying forward and backward could be split up like 21 * the ones for filling, i.e. in aligned and unaligned versions. This would 22 * help moving some redundant computations and branches out of the loop, too. 23 */ 24 25 #include <linux/module.h> 26 #include <linux/kernel.h> 27 #include <linux/string.h> 28 #include <linux/fb.h> 29 #include <asm/types.h> 30 #include <asm/io.h> 31 #include "fb_draw.h" 32 33 #if BITS_PER_LONG == 32 34 # define FB_WRITEL fb_writel 35 # define FB_READL fb_readl 36 #else 37 # define FB_WRITEL fb_writeq 38 # define FB_READL fb_readq 39 #endif 40 41 /* 42 * Generic bitwise copy algorithm 43 */ 44 45 static void 46 bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx, 47 const unsigned long __iomem *src, unsigned src_idx, int bits, 48 unsigned n, u32 bswapmask) 49 { 50 unsigned long first, last; 51 int const shift = dst_idx-src_idx; 52 53 #if 0 54 /* 55 * If you suspect bug in this function, compare it with this simple 56 * memmove implementation. 57 */ 58 memmove((char *)dst + ((dst_idx & (bits - 1))) / 8, 59 (char *)src + ((src_idx & (bits - 1))) / 8, n / 8); 60 return; 61 #endif 62 63 first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask); 64 last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask); 65 66 if (!shift) { 67 // Same alignment for source and dest 68 69 if (dst_idx+n <= bits) { 70 // Single word 71 if (last) 72 first &= last; 73 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst); 74 } else { 75 // Multiple destination words 76 77 // Leading bits 78 if (first != ~0UL) { 79 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst); 80 dst++; 81 src++; 82 n -= bits - dst_idx; 83 } 84 85 // Main chunk 86 n /= bits; 87 while (n >= 8) { 88 FB_WRITEL(FB_READL(src++), dst++); 89 FB_WRITEL(FB_READL(src++), dst++); 90 FB_WRITEL(FB_READL(src++), dst++); 91 FB_WRITEL(FB_READL(src++), dst++); 92 FB_WRITEL(FB_READL(src++), dst++); 93 FB_WRITEL(FB_READL(src++), dst++); 94 FB_WRITEL(FB_READL(src++), dst++); 95 FB_WRITEL(FB_READL(src++), dst++); 96 n -= 8; 97 } 98 while (n--) 99 FB_WRITEL(FB_READL(src++), dst++); 100 101 // Trailing bits 102 if (last) 103 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst); 104 } 105 } else { 106 /* Different alignment for source and dest */ 107 unsigned long d0, d1; 108 int m; 109 110 int const left = shift & (bits - 1); 111 int const right = -shift & (bits - 1); 112 113 if (dst_idx+n <= bits) { 114 // Single destination word 115 if (last) 116 first &= last; 117 d0 = FB_READL(src); 118 d0 = fb_rev_pixels_in_long(d0, bswapmask); 119 if (shift > 0) { 120 // Single source word 121 d0 <<= left; 122 } else if (src_idx+n <= bits) { 123 // Single source word 124 d0 >>= right; 125 } else { 126 // 2 source words 127 d1 = FB_READL(src + 1); 128 d1 = fb_rev_pixels_in_long(d1, bswapmask); 129 d0 = d0 >> right | d1 << left; 130 } 131 d0 = fb_rev_pixels_in_long(d0, bswapmask); 132 FB_WRITEL(comp(d0, FB_READL(dst), first), dst); 133 } else { 134 // Multiple destination words 135 /** We must always remember the last value read, because in case 136 SRC and DST overlap bitwise (e.g. when moving just one pixel in 137 1bpp), we always collect one full long for DST and that might 138 overlap with the current long from SRC. We store this value in 139 'd0'. */ 140 d0 = FB_READL(src++); 141 d0 = fb_rev_pixels_in_long(d0, bswapmask); 142 // Leading bits 143 if (shift > 0) { 144 // Single source word 145 d1 = d0; 146 d0 <<= left; 147 n -= bits - dst_idx; 148 } else { 149 // 2 source words 150 d1 = FB_READL(src++); 151 d1 = fb_rev_pixels_in_long(d1, bswapmask); 152 153 d0 = d0 >> right | d1 << left; 154 n -= bits - dst_idx; 155 } 156 d0 = fb_rev_pixels_in_long(d0, bswapmask); 157 FB_WRITEL(comp(d0, FB_READL(dst), first), dst); 158 d0 = d1; 159 dst++; 160 161 // Main chunk 162 m = n % bits; 163 n /= bits; 164 while ((n >= 4) && !bswapmask) { 165 d1 = FB_READL(src++); 166 FB_WRITEL(d0 >> right | d1 << left, dst++); 167 d0 = d1; 168 d1 = FB_READL(src++); 169 FB_WRITEL(d0 >> right | d1 << left, dst++); 170 d0 = d1; 171 d1 = FB_READL(src++); 172 FB_WRITEL(d0 >> right | d1 << left, dst++); 173 d0 = d1; 174 d1 = FB_READL(src++); 175 FB_WRITEL(d0 >> right | d1 << left, dst++); 176 d0 = d1; 177 n -= 4; 178 } 179 while (n--) { 180 d1 = FB_READL(src++); 181 d1 = fb_rev_pixels_in_long(d1, bswapmask); 182 d0 = d0 >> right | d1 << left; 183 d0 = fb_rev_pixels_in_long(d0, bswapmask); 184 FB_WRITEL(d0, dst++); 185 d0 = d1; 186 } 187 188 // Trailing bits 189 if (m) { 190 if (m <= bits - right) { 191 // Single source word 192 d0 >>= right; 193 } else { 194 // 2 source words 195 d1 = FB_READL(src); 196 d1 = fb_rev_pixels_in_long(d1, 197 bswapmask); 198 d0 = d0 >> right | d1 << left; 199 } 200 d0 = fb_rev_pixels_in_long(d0, bswapmask); 201 FB_WRITEL(comp(d0, FB_READL(dst), last), dst); 202 } 203 } 204 } 205 } 206 207 /* 208 * Generic bitwise copy algorithm, operating backward 209 */ 210 211 static void 212 bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx, 213 const unsigned long __iomem *src, unsigned src_idx, int bits, 214 unsigned n, u32 bswapmask) 215 { 216 unsigned long first, last; 217 int shift; 218 219 #if 0 220 /* 221 * If you suspect bug in this function, compare it with this simple 222 * memmove implementation. 223 */ 224 memmove((char *)dst + ((dst_idx & (bits - 1))) / 8, 225 (char *)src + ((src_idx & (bits - 1))) / 8, n / 8); 226 return; 227 #endif 228 229 dst += (dst_idx + n - 1) / bits; 230 src += (src_idx + n - 1) / bits; 231 dst_idx = (dst_idx + n - 1) % bits; 232 src_idx = (src_idx + n - 1) % bits; 233 234 shift = dst_idx-src_idx; 235 236 first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask); 237 last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask); 238 239 if (!shift) { 240 // Same alignment for source and dest 241 242 if ((unsigned long)dst_idx+1 >= n) { 243 // Single word 244 if (first) 245 last &= first; 246 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst); 247 } else { 248 // Multiple destination words 249 250 // Leading bits 251 if (first) { 252 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst); 253 dst--; 254 src--; 255 n -= dst_idx+1; 256 } 257 258 // Main chunk 259 n /= bits; 260 while (n >= 8) { 261 FB_WRITEL(FB_READL(src--), dst--); 262 FB_WRITEL(FB_READL(src--), dst--); 263 FB_WRITEL(FB_READL(src--), dst--); 264 FB_WRITEL(FB_READL(src--), dst--); 265 FB_WRITEL(FB_READL(src--), dst--); 266 FB_WRITEL(FB_READL(src--), dst--); 267 FB_WRITEL(FB_READL(src--), dst--); 268 FB_WRITEL(FB_READL(src--), dst--); 269 n -= 8; 270 } 271 while (n--) 272 FB_WRITEL(FB_READL(src--), dst--); 273 274 // Trailing bits 275 if (last != -1UL) 276 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst); 277 } 278 } else { 279 // Different alignment for source and dest 280 unsigned long d0, d1; 281 int m; 282 283 int const left = shift & (bits-1); 284 int const right = -shift & (bits-1); 285 286 if ((unsigned long)dst_idx+1 >= n) { 287 // Single destination word 288 if (first) 289 last &= first; 290 d0 = FB_READL(src); 291 if (shift < 0) { 292 // Single source word 293 d0 >>= right; 294 } else if (1+(unsigned long)src_idx >= n) { 295 // Single source word 296 d0 <<= left; 297 } else { 298 // 2 source words 299 d1 = FB_READL(src - 1); 300 d1 = fb_rev_pixels_in_long(d1, bswapmask); 301 d0 = d0 << left | d1 >> right; 302 } 303 d0 = fb_rev_pixels_in_long(d0, bswapmask); 304 FB_WRITEL(comp(d0, FB_READL(dst), last), dst); 305 } else { 306 // Multiple destination words 307 /** We must always remember the last value read, because in case 308 SRC and DST overlap bitwise (e.g. when moving just one pixel in 309 1bpp), we always collect one full long for DST and that might 310 overlap with the current long from SRC. We store this value in 311 'd0'. */ 312 313 d0 = FB_READL(src--); 314 d0 = fb_rev_pixels_in_long(d0, bswapmask); 315 // Leading bits 316 if (shift < 0) { 317 // Single source word 318 d1 = d0; 319 d0 >>= right; 320 } else { 321 // 2 source words 322 d1 = FB_READL(src--); 323 d1 = fb_rev_pixels_in_long(d1, bswapmask); 324 d0 = d0 << left | d1 >> right; 325 } 326 d0 = fb_rev_pixels_in_long(d0, bswapmask); 327 if (!first) 328 FB_WRITEL(d0, dst); 329 else 330 FB_WRITEL(comp(d0, FB_READL(dst), first), dst); 331 d0 = d1; 332 dst--; 333 n -= dst_idx+1; 334 335 // Main chunk 336 m = n % bits; 337 n /= bits; 338 while ((n >= 4) && !bswapmask) { 339 d1 = FB_READL(src--); 340 FB_WRITEL(d0 << left | d1 >> right, dst--); 341 d0 = d1; 342 d1 = FB_READL(src--); 343 FB_WRITEL(d0 << left | d1 >> right, dst--); 344 d0 = d1; 345 d1 = FB_READL(src--); 346 FB_WRITEL(d0 << left | d1 >> right, dst--); 347 d0 = d1; 348 d1 = FB_READL(src--); 349 FB_WRITEL(d0 << left | d1 >> right, dst--); 350 d0 = d1; 351 n -= 4; 352 } 353 while (n--) { 354 d1 = FB_READL(src--); 355 d1 = fb_rev_pixels_in_long(d1, bswapmask); 356 d0 = d0 << left | d1 >> right; 357 d0 = fb_rev_pixels_in_long(d0, bswapmask); 358 FB_WRITEL(d0, dst--); 359 d0 = d1; 360 } 361 362 // Trailing bits 363 if (m) { 364 if (m <= bits - left) { 365 // Single source word 366 d0 <<= left; 367 } else { 368 // 2 source words 369 d1 = FB_READL(src); 370 d1 = fb_rev_pixels_in_long(d1, 371 bswapmask); 372 d0 = d0 << left | d1 >> right; 373 } 374 d0 = fb_rev_pixels_in_long(d0, bswapmask); 375 FB_WRITEL(comp(d0, FB_READL(dst), last), dst); 376 } 377 } 378 } 379 } 380 381 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) 382 { 383 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy; 384 u32 height = area->height, width = area->width; 385 unsigned int const bits_per_line = p->fix.line_length * 8u; 386 unsigned long __iomem *base = NULL; 387 int bits = BITS_PER_LONG, bytes = bits >> 3; 388 unsigned dst_idx = 0, src_idx = 0, rev_copy = 0; 389 u32 bswapmask = fb_compute_bswapmask(p); 390 391 if (p->state != FBINFO_STATE_RUNNING) 392 return; 393 394 /* if the beginning of the target area might overlap with the end of 395 the source area, be have to copy the area reverse. */ 396 if ((dy == sy && dx > sx) || (dy > sy)) { 397 dy += height; 398 sy += height; 399 rev_copy = 1; 400 } 401 402 // split the base of the framebuffer into a long-aligned address and the 403 // index of the first bit 404 base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1)); 405 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1)); 406 // add offset of source and target area 407 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel; 408 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel; 409 410 if (p->fbops->fb_sync) 411 p->fbops->fb_sync(p); 412 413 if (rev_copy) { 414 while (height--) { 415 dst_idx -= bits_per_line; 416 src_idx -= bits_per_line; 417 bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits, 418 base + (src_idx / bits), src_idx % bits, bits, 419 width*p->var.bits_per_pixel, bswapmask); 420 } 421 } else { 422 while (height--) { 423 bitcpy(p, base + (dst_idx / bits), dst_idx % bits, 424 base + (src_idx / bits), src_idx % bits, bits, 425 width*p->var.bits_per_pixel, bswapmask); 426 dst_idx += bits_per_line; 427 src_idx += bits_per_line; 428 } 429 } 430 } 431 432 EXPORT_SYMBOL(cfb_copyarea); 433 434 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>"); 435 MODULE_DESCRIPTION("Generic software accelerated copyarea"); 436 MODULE_LICENSE("GPL"); 437 438