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