Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  *  Generic function for frame buffer with packed pixels of any depth.
0003  *
0004  *      Copyright (C)  1999-2005 James Simmons <jsimmons@www.infradead.org>
0005  *
0006  *  This file is subject to the terms and conditions of the GNU General Public
0007  *  License.  See the file COPYING in the main directory of this archive for
0008  *  more details.
0009  *
0010  * NOTES:
0011  *
0012  *  This is for cfb packed pixels. Iplan and such are incorporated in the
0013  *  drivers that need them.
0014  *
0015  *  FIXME
0016  *
0017  *  Also need to add code to deal with cards endians that are different than
0018  *  the native cpu endians. I also need to deal with MSB position in the word.
0019  *
0020  *  The two functions or copying forward and backward could be split up like
0021  *  the ones for filling, i.e. in aligned and unaligned versions. This would
0022  *  help moving some redundant computations and branches out of the loop, too.
0023  */
0024 
0025 #include <linux/module.h>
0026 #include <linux/kernel.h>
0027 #include <linux/string.h>
0028 #include <linux/fb.h>
0029 #include <asm/types.h>
0030 #include <asm/io.h>
0031 #include "fb_draw.h"
0032 
0033 #if BITS_PER_LONG == 32
0034 #  define FB_WRITEL fb_writel
0035 #  define FB_READL  fb_readl
0036 #else
0037 #  define FB_WRITEL fb_writeq
0038 #  define FB_READL  fb_readq
0039 #endif
0040 
0041     /*
0042      *  Generic bitwise copy algorithm
0043      */
0044 
0045 static void
0046 bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
0047         const unsigned long __iomem *src, unsigned src_idx, int bits,
0048         unsigned n, u32 bswapmask)
0049 {
0050     unsigned long first, last;
0051     int const shift = dst_idx-src_idx;
0052 
0053 #if 0
0054     /*
0055      * If you suspect bug in this function, compare it with this simple
0056      * memmove implementation.
0057      */
0058     memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
0059         (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
0060     return;
0061 #endif
0062 
0063     first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
0064     last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
0065 
0066     if (!shift) {
0067         // Same alignment for source and dest
0068 
0069         if (dst_idx+n <= bits) {
0070             // Single word
0071             if (last)
0072                 first &= last;
0073             FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
0074         } else {
0075             // Multiple destination words
0076 
0077             // Leading bits
0078             if (first != ~0UL) {
0079                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
0080                 dst++;
0081                 src++;
0082                 n -= bits - dst_idx;
0083             }
0084 
0085             // Main chunk
0086             n /= bits;
0087             while (n >= 8) {
0088                 FB_WRITEL(FB_READL(src++), dst++);
0089                 FB_WRITEL(FB_READL(src++), dst++);
0090                 FB_WRITEL(FB_READL(src++), dst++);
0091                 FB_WRITEL(FB_READL(src++), dst++);
0092                 FB_WRITEL(FB_READL(src++), dst++);
0093                 FB_WRITEL(FB_READL(src++), dst++);
0094                 FB_WRITEL(FB_READL(src++), dst++);
0095                 FB_WRITEL(FB_READL(src++), dst++);
0096                 n -= 8;
0097             }
0098             while (n--)
0099                 FB_WRITEL(FB_READL(src++), dst++);
0100 
0101             // Trailing bits
0102             if (last)
0103                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
0104         }
0105     } else {
0106         /* Different alignment for source and dest */
0107         unsigned long d0, d1;
0108         int m;
0109 
0110         int const left = shift & (bits - 1);
0111         int const right = -shift & (bits - 1);
0112 
0113         if (dst_idx+n <= bits) {
0114             // Single destination word
0115             if (last)
0116                 first &= last;
0117             d0 = FB_READL(src);
0118             d0 = fb_rev_pixels_in_long(d0, bswapmask);
0119             if (shift > 0) {
0120                 // Single source word
0121                 d0 <<= left;
0122             } else if (src_idx+n <= bits) {
0123                 // Single source word
0124                 d0 >>= right;
0125             } else {
0126                 // 2 source words
0127                 d1 = FB_READL(src + 1);
0128                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
0129                 d0 = d0 >> right | d1 << left;
0130             }
0131             d0 = fb_rev_pixels_in_long(d0, bswapmask);
0132             FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
0133         } else {
0134             // Multiple destination words
0135             /** We must always remember the last value read, because in case
0136             SRC and DST overlap bitwise (e.g. when moving just one pixel in
0137             1bpp), we always collect one full long for DST and that might
0138             overlap with the current long from SRC. We store this value in
0139             'd0'. */
0140             d0 = FB_READL(src++);
0141             d0 = fb_rev_pixels_in_long(d0, bswapmask);
0142             // Leading bits
0143             if (shift > 0) {
0144                 // Single source word
0145                 d1 = d0;
0146                 d0 <<= left;
0147                 n -= bits - dst_idx;
0148             } else {
0149                 // 2 source words
0150                 d1 = FB_READL(src++);
0151                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
0152 
0153                 d0 = d0 >> right | d1 << left;
0154                 n -= bits - dst_idx;
0155             }
0156             d0 = fb_rev_pixels_in_long(d0, bswapmask);
0157             FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
0158             d0 = d1;
0159             dst++;
0160 
0161             // Main chunk
0162             m = n % bits;
0163             n /= bits;
0164             while ((n >= 4) && !bswapmask) {
0165                 d1 = FB_READL(src++);
0166                 FB_WRITEL(d0 >> right | d1 << left, dst++);
0167                 d0 = d1;
0168                 d1 = FB_READL(src++);
0169                 FB_WRITEL(d0 >> right | d1 << left, dst++);
0170                 d0 = d1;
0171                 d1 = FB_READL(src++);
0172                 FB_WRITEL(d0 >> right | d1 << left, dst++);
0173                 d0 = d1;
0174                 d1 = FB_READL(src++);
0175                 FB_WRITEL(d0 >> right | d1 << left, dst++);
0176                 d0 = d1;
0177                 n -= 4;
0178             }
0179             while (n--) {
0180                 d1 = FB_READL(src++);
0181                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
0182                 d0 = d0 >> right | d1 << left;
0183                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
0184                 FB_WRITEL(d0, dst++);
0185                 d0 = d1;
0186             }
0187 
0188             // Trailing bits
0189             if (m) {
0190                 if (m <= bits - right) {
0191                     // Single source word
0192                     d0 >>= right;
0193                 } else {
0194                     // 2 source words
0195                     d1 = FB_READL(src);
0196                     d1 = fb_rev_pixels_in_long(d1,
0197                                 bswapmask);
0198                     d0 = d0 >> right | d1 << left;
0199                 }
0200                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
0201                 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
0202             }
0203         }
0204     }
0205 }
0206 
0207     /*
0208      *  Generic bitwise copy algorithm, operating backward
0209      */
0210 
0211 static void
0212 bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
0213         const unsigned long __iomem *src, unsigned src_idx, int bits,
0214         unsigned n, u32 bswapmask)
0215 {
0216     unsigned long first, last;
0217     int shift;
0218 
0219 #if 0
0220     /*
0221      * If you suspect bug in this function, compare it with this simple
0222      * memmove implementation.
0223      */
0224     memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
0225         (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
0226     return;
0227 #endif
0228 
0229     dst += (dst_idx + n - 1) / bits;
0230     src += (src_idx + n - 1) / bits;
0231     dst_idx = (dst_idx + n - 1) % bits;
0232     src_idx = (src_idx + n - 1) % bits;
0233 
0234     shift = dst_idx-src_idx;
0235 
0236     first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask);
0237     last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask);
0238 
0239     if (!shift) {
0240         // Same alignment for source and dest
0241 
0242         if ((unsigned long)dst_idx+1 >= n) {
0243             // Single word
0244             if (first)
0245                 last &= first;
0246             FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
0247         } else {
0248             // Multiple destination words
0249 
0250             // Leading bits
0251             if (first) {
0252                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
0253                 dst--;
0254                 src--;
0255                 n -= dst_idx+1;
0256             }
0257 
0258             // Main chunk
0259             n /= bits;
0260             while (n >= 8) {
0261                 FB_WRITEL(FB_READL(src--), dst--);
0262                 FB_WRITEL(FB_READL(src--), dst--);
0263                 FB_WRITEL(FB_READL(src--), dst--);
0264                 FB_WRITEL(FB_READL(src--), dst--);
0265                 FB_WRITEL(FB_READL(src--), dst--);
0266                 FB_WRITEL(FB_READL(src--), dst--);
0267                 FB_WRITEL(FB_READL(src--), dst--);
0268                 FB_WRITEL(FB_READL(src--), dst--);
0269                 n -= 8;
0270             }
0271             while (n--)
0272                 FB_WRITEL(FB_READL(src--), dst--);
0273 
0274             // Trailing bits
0275             if (last != -1UL)
0276                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
0277         }
0278     } else {
0279         // Different alignment for source and dest
0280         unsigned long d0, d1;
0281         int m;
0282 
0283         int const left = shift & (bits-1);
0284         int const right = -shift & (bits-1);
0285 
0286         if ((unsigned long)dst_idx+1 >= n) {
0287             // Single destination word
0288             if (first)
0289                 last &= first;
0290             d0 = FB_READL(src);
0291             if (shift < 0) {
0292                 // Single source word
0293                 d0 >>= right;
0294             } else if (1+(unsigned long)src_idx >= n) {
0295                 // Single source word
0296                 d0 <<= left;
0297             } else {
0298                 // 2 source words
0299                 d1 = FB_READL(src - 1);
0300                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
0301                 d0 = d0 << left | d1 >> right;
0302             }
0303             d0 = fb_rev_pixels_in_long(d0, bswapmask);
0304             FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
0305         } else {
0306             // Multiple destination words
0307             /** We must always remember the last value read, because in case
0308             SRC and DST overlap bitwise (e.g. when moving just one pixel in
0309             1bpp), we always collect one full long for DST and that might
0310             overlap with the current long from SRC. We store this value in
0311             'd0'. */
0312 
0313             d0 = FB_READL(src--);
0314             d0 = fb_rev_pixels_in_long(d0, bswapmask);
0315             // Leading bits
0316             if (shift < 0) {
0317                 // Single source word
0318                 d1 = d0;
0319                 d0 >>= right;
0320             } else {
0321                 // 2 source words
0322                 d1 = FB_READL(src--);
0323                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
0324                 d0 = d0 << left | d1 >> right;
0325             }
0326             d0 = fb_rev_pixels_in_long(d0, bswapmask);
0327             if (!first)
0328                 FB_WRITEL(d0, dst);
0329             else
0330                 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
0331             d0 = d1;
0332             dst--;
0333             n -= dst_idx+1;
0334 
0335             // Main chunk
0336             m = n % bits;
0337             n /= bits;
0338             while ((n >= 4) && !bswapmask) {
0339                 d1 = FB_READL(src--);
0340                 FB_WRITEL(d0 << left | d1 >> right, dst--);
0341                 d0 = d1;
0342                 d1 = FB_READL(src--);
0343                 FB_WRITEL(d0 << left | d1 >> right, dst--);
0344                 d0 = d1;
0345                 d1 = FB_READL(src--);
0346                 FB_WRITEL(d0 << left | d1 >> right, dst--);
0347                 d0 = d1;
0348                 d1 = FB_READL(src--);
0349                 FB_WRITEL(d0 << left | d1 >> right, dst--);
0350                 d0 = d1;
0351                 n -= 4;
0352             }
0353             while (n--) {
0354                 d1 = FB_READL(src--);
0355                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
0356                 d0 = d0 << left | d1 >> right;
0357                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
0358                 FB_WRITEL(d0, dst--);
0359                 d0 = d1;
0360             }
0361 
0362             // Trailing bits
0363             if (m) {
0364                 if (m <= bits - left) {
0365                     // Single source word
0366                     d0 <<= left;
0367                 } else {
0368                     // 2 source words
0369                     d1 = FB_READL(src);
0370                     d1 = fb_rev_pixels_in_long(d1,
0371                                 bswapmask);
0372                     d0 = d0 << left | d1 >> right;
0373                 }
0374                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
0375                 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
0376             }
0377         }
0378     }
0379 }
0380 
0381 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
0382 {
0383     u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
0384     u32 height = area->height, width = area->width;
0385     unsigned long const bits_per_line = p->fix.line_length*8u;
0386     unsigned long __iomem *base = NULL;
0387     int bits = BITS_PER_LONG, bytes = bits >> 3;
0388     unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
0389     u32 bswapmask = fb_compute_bswapmask(p);
0390 
0391     if (p->state != FBINFO_STATE_RUNNING)
0392         return;
0393 
0394     /* if the beginning of the target area might overlap with the end of
0395     the source area, be have to copy the area reverse. */
0396     if ((dy == sy && dx > sx) || (dy > sy)) {
0397         dy += height;
0398         sy += height;
0399         rev_copy = 1;
0400     }
0401 
0402     // split the base of the framebuffer into a long-aligned address and the
0403     // index of the first bit
0404     base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
0405     dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
0406     // add offset of source and target area
0407     dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
0408     src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
0409 
0410     if (p->fbops->fb_sync)
0411         p->fbops->fb_sync(p);
0412 
0413     if (rev_copy) {
0414         while (height--) {
0415             dst_idx -= bits_per_line;
0416             src_idx -= bits_per_line;
0417             bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
0418                 base + (src_idx / bits), src_idx % bits, bits,
0419                 width*p->var.bits_per_pixel, bswapmask);
0420         }
0421     } else {
0422         while (height--) {
0423             bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
0424                 base + (src_idx / bits), src_idx % bits, bits,
0425                 width*p->var.bits_per_pixel, bswapmask);
0426             dst_idx += bits_per_line;
0427             src_idx += bits_per_line;
0428         }
0429     }
0430 }
0431 
0432 EXPORT_SYMBOL(cfb_copyarea);
0433 
0434 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
0435 MODULE_DESCRIPTION("Generic software accelerated copyarea");
0436 MODULE_LICENSE("GPL");
0437