441 lines · c
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 Public7 * License. See the file COPYING in the main directory of this archive for8 * more details.9 *10 * NOTES:11 *12 * This is for cfb packed pixels. Iplan and such are incorporated in the13 * drivers that need them.14 *15 * FIXME16 *17 * Also need to add code to deal with cards endians that are different than18 * 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 like21 * the ones for filling, i.e. in aligned and unaligned versions. This would22 * 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 == 3234# define FB_WRITEL fb_writel35# define FB_READL fb_readl36#else37# define FB_WRITEL fb_writeq38# define FB_READL fb_readq39#endif40 41 /*42 * Generic bitwise copy algorithm43 */44 45static void46bitcpy(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 054 /*55 * If you suspect bug in this function, compare it with this simple56 * 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#endif62 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 dest68 69 if (dst_idx+n <= bits) {70 // Single word71 if (last)72 first &= last;73 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);74 } else {75 // Multiple destination words76 77 // Leading bits78 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 chunk86 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 bits102 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 word115 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 word121 d0 <<= left;122 } else if (src_idx+n <= bits) {123 // Single source word124 d0 >>= right;125 } else {126 // 2 source words127 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 words135 /** We must always remember the last value read, because in case136 SRC and DST overlap bitwise (e.g. when moving just one pixel in137 1bpp), we always collect one full long for DST and that might138 overlap with the current long from SRC. We store this value in139 'd0'. */140 d0 = FB_READL(src++);141 d0 = fb_rev_pixels_in_long(d0, bswapmask);142 // Leading bits143 if (shift > 0) {144 // Single source word145 d1 = d0;146 d0 <<= left;147 n -= bits - dst_idx;148 } else {149 // 2 source words150 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 chunk162 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 bits189 if (m) {190 if (m <= bits - right) {191 // Single source word192 d0 >>= right;193 } else {194 // 2 source words195 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 backward209 */210 211static void212bitcpy_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 0220 /*221 * If you suspect bug in this function, compare it with this simple222 * 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#endif228 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 dest241 242 if ((unsigned long)dst_idx+1 >= n) {243 // Single word244 if (first)245 last &= first;246 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);247 } else {248 // Multiple destination words249 250 // Leading bits251 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 chunk259 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 bits275 if (last != -1UL)276 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);277 }278 } else {279 // Different alignment for source and dest280 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 word288 if (first)289 last &= first;290 d0 = FB_READL(src);291 if (shift < 0) {292 // Single source word293 d0 >>= right;294 } else if (1+(unsigned long)src_idx >= n) {295 // Single source word296 d0 <<= left;297 } else {298 // 2 source words299 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 words307 /** We must always remember the last value read, because in case308 SRC and DST overlap bitwise (e.g. when moving just one pixel in309 1bpp), we always collect one full long for DST and that might310 overlap with the current long from SRC. We store this value in311 'd0'. */312 313 d0 = FB_READL(src--);314 d0 = fb_rev_pixels_in_long(d0, bswapmask);315 // Leading bits316 if (shift < 0) {317 // Single source word318 d1 = d0;319 d0 >>= right;320 } else {321 // 2 source words322 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 else330 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);331 d0 = d1;332 dst--;333 n -= dst_idx+1;334 335 // Main chunk336 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 bits363 if (m) {364 if (m <= bits - left) {365 // Single source word366 d0 <<= left;367 } else {368 // 2 source words369 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 381void 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 (p->flags & FBINFO_VIRTFB)395 fb_warn_once(p, "Framebuffer is not in I/O address space.");396 397 /* if the beginning of the target area might overlap with the end of398 the source area, be have to copy the area reverse. */399 if ((dy == sy && dx > sx) || (dy > sy)) {400 dy += height;401 sy += height;402 rev_copy = 1;403 }404 405 // split the base of the framebuffer into a long-aligned address and the406 // index of the first bit407 base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));408 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));409 // add offset of source and target area410 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;411 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;412 413 if (p->fbops->fb_sync)414 p->fbops->fb_sync(p);415 416 if (rev_copy) {417 while (height--) {418 dst_idx -= bits_per_line;419 src_idx -= bits_per_line;420 bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,421 base + (src_idx / bits), src_idx % bits, bits,422 width*p->var.bits_per_pixel, bswapmask);423 }424 } else {425 while (height--) {426 bitcpy(p, base + (dst_idx / bits), dst_idx % bits,427 base + (src_idx / bits), src_idx % bits, bits,428 width*p->var.bits_per_pixel, bswapmask);429 dst_idx += bits_per_line;430 src_idx += bits_per_line;431 }432 }433}434 435EXPORT_SYMBOL(cfb_copyarea);436 437MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");438MODULE_DESCRIPTION("Generic software accelerated copyarea");439MODULE_LICENSE("GPL");440 441