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