viafb: do modesetting after updating variables
[safe/jmp/linux-2.6] / drivers / video / cfbcopyarea.c
index 67711f7..79e5f40 100644 (file)
@@ -22,9 +22,6 @@
  *  help moving some redundant computations and branches out of the loop, too.
  */
 
-
-
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
@@ -32,6 +29,7 @@
 #include <linux/slab.h>
 #include <asm/types.h>
 #include <asm/io.h>
+#include "fb_draw.h"
 
 #if BITS_PER_LONG == 32
 #  define FB_WRITEL fb_writel
 #endif
 
     /*
-     *  Compose two values, using a bitmask as decision value
-     *  This is equivalent to (a & mask) | (b & ~mask)
-     */
-
-static inline unsigned long
-comp(unsigned long a, unsigned long b, unsigned long mask)
-{
-    return ((a ^ b) & mask) ^ b;
-}
-
-    /*
      *  Generic bitwise copy algorithm
      */
 
 static void
-bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
-       int src_idx, int bits, unsigned n)
+bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
+               const unsigned long __iomem *src, int src_idx, int bits,
+               unsigned n, u32 bswapmask)
 {
        unsigned long first, last;
        int const shift = dst_idx-src_idx;
        int left, right;
 
-       first = ~0UL >> dst_idx;
-       last = ~(~0UL >> ((dst_idx+n) % bits));
+       first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
+       last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
 
        if (!shift) {
                // Same alignment for source and dest
@@ -107,29 +95,34 @@ bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src
                                FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
                }
        } else {
+               /* Different alignment for source and dest */
                unsigned long d0, d1;
                int m;
-               // Different alignment for source and dest
 
                right = shift & (bits - 1);
                left = -shift & (bits - 1);
+               bswapmask &= shift;
 
                if (dst_idx+n <= bits) {
                        // Single destination word
                        if (last)
                                first &= last;
+                       d0 = FB_READL(src);
+                       d0 = fb_rev_pixels_in_long(d0, bswapmask);
                        if (shift > 0) {
                                // Single source word
-                               FB_WRITEL( comp( FB_READL(src) >> right, FB_READL(dst), first), dst);
+                               d0 >>= right;
                        } else if (src_idx+n <= bits) {
                                // Single source word
-                               FB_WRITEL( comp(FB_READL(src) << left, FB_READL(dst), first), dst);
+                               d0 <<= left;
                        } else {
                                // 2 source words
-                               d0 = FB_READL(src++);
-                               d1 = FB_READL(src);
-                               FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
+                               d1 = FB_READL(src + 1);
+                               d1 = fb_rev_pixels_in_long(d1, bswapmask);
+                               d0 = d0<<left | d1>>right;
                        }
+                       d0 = fb_rev_pixels_in_long(d0, bswapmask);
+                       FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
                } else {
                        // Multiple destination words
                        /** We must always remember the last value read, because in case
@@ -138,25 +131,31 @@ bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src
                        overlap with the current long from SRC. We store this value in
                        'd0'. */
                        d0 = FB_READL(src++);
+                       d0 = fb_rev_pixels_in_long(d0, bswapmask);
                        // Leading bits
                        if (shift > 0) {
                                // Single source word
-                               FB_WRITEL( comp(d0 >> right, FB_READL(dst), first), dst);
+                               d1 = d0;
+                               d0 >>= right;
                                dst++;
                                n -= bits - dst_idx;
                        } else {
                                // 2 source words
                                d1 = FB_READL(src++);
-                               FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
-                               d0 = d1;
+                               d1 = fb_rev_pixels_in_long(d1, bswapmask);
+
+                               d0 = d0<<left | d1>>right;
                                dst++;
                                n -= bits - dst_idx;
                        }
+                       d0 = fb_rev_pixels_in_long(d0, bswapmask);
+                       FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
+                       d0 = d1;
 
                        // Main chunk
                        m = n % bits;
                        n /= bits;
-                       while (n >= 4) {
+                       while ((n >= 4) && !bswapmask) {
                                d1 = FB_READL(src++);
                                FB_WRITEL(d0 << left | d1 >> right, dst++);
                                d0 = d1;
@@ -173,7 +172,10 @@ bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src
                        }
                        while (n--) {
                                d1 = FB_READL(src++);
-                               FB_WRITEL(d0 << left | d1 >> right, dst++);
+                               d1 = fb_rev_pixels_in_long(d1, bswapmask);
+                               d0 = d0 << left | d1 >> right;
+                               d0 = fb_rev_pixels_in_long(d0, bswapmask);
+                               FB_WRITEL(d0, dst++);
                                d0 = d1;
                        }
 
@@ -181,12 +183,16 @@ bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src
                        if (last) {
                                if (m <= right) {
                                        // Single source word
-                                       FB_WRITEL( comp(d0 << left, FB_READL(dst), last), dst);
+                                       d0 <<= left;
                                } else {
                                        // 2 source words
                                        d1 = FB_READL(src);
-                                       FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), last), dst);
+                                       d1 = fb_rev_pixels_in_long(d1,
+                                                               bswapmask);
+                                       d0 = d0<<left | d1>>right;
                                }
+                               d0 = fb_rev_pixels_in_long(d0, bswapmask);
+                               FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
                        }
                }
        }
@@ -197,8 +203,9 @@ bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src
      */
 
 static void
-bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
-               int src_idx, int bits, unsigned n)
+bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
+               const unsigned long __iomem *src, int src_idx, int bits,
+               unsigned n, u32 bswapmask)
 {
        unsigned long first, last;
        int shift;
@@ -216,8 +223,9 @@ bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem
 
        shift = dst_idx-src_idx;
 
-       first = ~0UL << (bits - 1 - dst_idx);
-       last = ~(~0UL << (bits - 1 - ((dst_idx-n) % bits)));
+       first = fb_shifted_pixels_mask_long(p, bits - 1 - dst_idx, bswapmask);
+       last = ~fb_shifted_pixels_mask_long(p, bits - 1 - ((dst_idx-n) % bits),
+                                           bswapmask);
 
        if (!shift) {
                // Same alignment for source and dest
@@ -260,24 +268,32 @@ bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem
                }
        } else {
                // Different alignment for source and dest
+               unsigned long d0, d1;
+               int m;
 
                int const left = -shift & (bits-1);
                int const right = shift & (bits-1);
+               bswapmask &= shift;
 
                if ((unsigned long)dst_idx+1 >= n) {
                        // Single destination word
                        if (last)
                                first &= last;
+                       d0 = FB_READL(src);
                        if (shift < 0) {
                                // Single source word
-                               FB_WRITEL( comp( FB_READL(src)<<left, FB_READL(dst), first), dst);
+                               d0 <<= left;
                        } else if (1+(unsigned long)src_idx >= n) {
                                // Single source word
-                               FB_WRITEL( comp( FB_READL(src)>>right, FB_READL(dst), first), dst);
+                               d0 >>= right;
                        } else {
                                // 2 source words
-                               FB_WRITEL( comp( (FB_READL(src)>>right | FB_READL(src-1)<<left), FB_READL(dst), first), dst);
+                               d1 = FB_READL(src - 1);
+                               d1 = fb_rev_pixels_in_long(d1, bswapmask);
+                               d0 = d0>>right | d1<<left;
                        }
+                       d0 = fb_rev_pixels_in_long(d0, bswapmask);
+                       FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
                } else {
                        // Multiple destination words
                        /** We must always remember the last value read, because in case
@@ -285,27 +301,30 @@ bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem
                        1bpp), we always collect one full long for DST and that might
                        overlap with the current long from SRC. We store this value in
                        'd0'. */
-                       unsigned long d0, d1;
-                       int m;
 
                        d0 = FB_READL(src--);
+                       d0 = fb_rev_pixels_in_long(d0, bswapmask);
                        // Leading bits
                        if (shift < 0) {
                                // Single source word
-                               FB_WRITEL( comp( (d0 << left), FB_READL(dst), first), dst);
+                               d1 = d0;
+                               d0 <<= left;
                        } else {
                                // 2 source words
                                d1 = FB_READL(src--);
-                               FB_WRITEL( comp( (d0>>right | d1<<left), FB_READL(dst), first), dst);
-                               d0 = d1;
+                               d1 = fb_rev_pixels_in_long(d1, bswapmask);
+                               d0 = d0>>right | d1<<left;
                        }
+                       d0 = fb_rev_pixels_in_long(d0, bswapmask);
+                       FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
+                       d0 = d1;
                        dst--;
                        n -= dst_idx+1;
 
                        // Main chunk
                        m = n % bits;
                        n /= bits;
-                       while (n >= 4) {
+                       while ((n >= 4) && !bswapmask) {
                                d1 = FB_READL(src--);
                                FB_WRITEL(d0 >> right | d1 << left, dst--);
                                d0 = d1;
@@ -322,7 +341,10 @@ bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem
                        }
                        while (n--) {
                                d1 = FB_READL(src--);
-                               FB_WRITEL(d0 >> right | d1 << left, dst--);
+                               d1 = fb_rev_pixels_in_long(d1, bswapmask);
+                               d0 = d0 >> right | d1 << left;
+                               d0 = fb_rev_pixels_in_long(d0, bswapmask);
+                               FB_WRITEL(d0, dst--);
                                d0 = d1;
                        }
 
@@ -330,12 +352,16 @@ bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem
                        if (last) {
                                if (m <= left) {
                                        // Single source word
-                                       FB_WRITEL( comp(d0 >> right, FB_READL(dst), last), dst);
+                                       d0 >>= right;
                                } else {
                                        // 2 source words
                                        d1 = FB_READL(src);
-                                       FB_WRITEL( comp(d0>>right | d1<<left, FB_READL(dst), last), dst);
+                                       d1 = fb_rev_pixels_in_long(d1,
+                                                               bswapmask);
+                                       d0 = d0>>right | d1<<left;
                                }
+                               d0 = fb_rev_pixels_in_long(d0, bswapmask);
+                               FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
                        }
                }
        }
@@ -349,46 +375,11 @@ void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
        unsigned long __iomem *dst = NULL, *src = NULL;
        int bits = BITS_PER_LONG, bytes = bits >> 3;
        int dst_idx = 0, src_idx = 0, rev_copy = 0;
-       int x2, y2, vxres, vyres;
+       u32 bswapmask = fb_compute_bswapmask(p);
 
        if (p->state != FBINFO_STATE_RUNNING)
                return;
 
-       /* We want rotation but lack hardware to do it for us. */
-       if (!p->fbops->fb_rotate && p->var.rotate) {
-       }
-
-       vxres = p->var.xres_virtual;
-       vyres = p->var.yres_virtual;
-
-       if (area->dx > vxres || area->sx > vxres ||
-           area->dy > vyres || area->sy > vyres)
-               return;
-
-       /* clip the destination
-        * We could use hardware clipping but on many cards you get around
-        * hardware clipping by writing to framebuffer directly.
-        */
-       x2 = area->dx + area->width;
-       y2 = area->dy + area->height;
-       dx = area->dx > 0 ? area->dx : 0;
-       dy = area->dy > 0 ? area->dy : 0;
-       x2 = x2 < vxres ? x2 : vxres;
-       y2 = y2 < vyres ? y2 : vyres;
-       width = x2 - dx;
-       height = y2 - dy;
-
-       if ((width==0) ||(height==0))
-               return;
-
-       /* update sx1,sy1 */
-       sx += (dx - area->dx);
-       sy += (dy - area->dy);
-
-       /* the source must be completely inside the virtual screen */
-       if (sx < 0 || sy < 0 || (sx + width) > vxres || (sy + height) > vyres)
-               return;
-
        /* if the beginning of the target area might overlap with the end of
        the source area, be have to copy the area reverse. */
        if ((dy == sy && dx > sx) || (dy > sy)) {
@@ -416,8 +407,8 @@ void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
                        dst_idx &= (bytes - 1);
                        src += src_idx >> (ffs(bits) - 1);
                        src_idx &= (bytes - 1);
-                       bitcpy_rev(dst, dst_idx, src, src_idx, bits,
-                               width*p->var.bits_per_pixel);
+                       bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
+                               width*p->var.bits_per_pixel, bswapmask);
                }
        } else {
                while (height--) {
@@ -425,8 +416,8 @@ void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
                        dst_idx &= (bytes - 1);
                        src += src_idx >> (ffs(bits) - 1);
                        src_idx &= (bytes - 1);
-                       bitcpy(dst, dst_idx, src, src_idx, bits,
-                               width*p->var.bits_per_pixel);
+                       bitcpy(p, dst, dst_idx, src, src_idx, bits,
+                               width*p->var.bits_per_pixel, bswapmask);
                        dst_idx += bits_per_line;
                        src_idx += bits_per_line;
                }