119757fc8STomi Valkeinen /*
219757fc8STomi Valkeinen  *  Generic function for frame buffer with packed pixels of any depth.
319757fc8STomi Valkeinen  *
419757fc8STomi Valkeinen  *      Copyright (C)  1999-2005 James Simmons <jsimmons@www.infradead.org>
519757fc8STomi Valkeinen  *
619757fc8STomi Valkeinen  *  This file is subject to the terms and conditions of the GNU General Public
719757fc8STomi Valkeinen  *  License.  See the file COPYING in the main directory of this archive for
819757fc8STomi Valkeinen  *  more details.
919757fc8STomi Valkeinen  *
1019757fc8STomi Valkeinen  * NOTES:
1119757fc8STomi Valkeinen  *
1219757fc8STomi Valkeinen  *  This is for cfb packed pixels. Iplan and such are incorporated in the
1319757fc8STomi Valkeinen  *  drivers that need them.
1419757fc8STomi Valkeinen  *
1519757fc8STomi Valkeinen  *  FIXME
1619757fc8STomi Valkeinen  *
1719757fc8STomi Valkeinen  *  Also need to add code to deal with cards endians that are different than
1819757fc8STomi Valkeinen  *  the native cpu endians. I also need to deal with MSB position in the word.
1919757fc8STomi Valkeinen  *
2019757fc8STomi Valkeinen  *  The two functions or copying forward and backward could be split up like
2119757fc8STomi Valkeinen  *  the ones for filling, i.e. in aligned and unaligned versions. This would
2219757fc8STomi Valkeinen  *  help moving some redundant computations and branches out of the loop, too.
2319757fc8STomi Valkeinen  */
2419757fc8STomi Valkeinen 
2519757fc8STomi Valkeinen #include <linux/module.h>
2619757fc8STomi Valkeinen #include <linux/kernel.h>
2719757fc8STomi Valkeinen #include <linux/string.h>
2819757fc8STomi Valkeinen #include <linux/fb.h>
2919757fc8STomi Valkeinen #include <asm/types.h>
3019757fc8STomi Valkeinen #include <asm/io.h>
3119757fc8STomi Valkeinen #include "fb_draw.h"
3219757fc8STomi Valkeinen 
3319757fc8STomi Valkeinen #if BITS_PER_LONG == 32
3419757fc8STomi Valkeinen #  define FB_WRITEL fb_writel
3519757fc8STomi Valkeinen #  define FB_READL  fb_readl
3619757fc8STomi Valkeinen #else
3719757fc8STomi Valkeinen #  define FB_WRITEL fb_writeq
3819757fc8STomi Valkeinen #  define FB_READL  fb_readq
3919757fc8STomi Valkeinen #endif
4019757fc8STomi Valkeinen 
4119757fc8STomi Valkeinen     /*
4219757fc8STomi Valkeinen      *  Generic bitwise copy algorithm
4319757fc8STomi Valkeinen      */
4419757fc8STomi Valkeinen 
4519757fc8STomi Valkeinen static void
bitcpy(struct fb_info * p,unsigned long __iomem * dst,unsigned dst_idx,const unsigned long __iomem * src,unsigned src_idx,int bits,unsigned n,u32 bswapmask)4619757fc8STomi Valkeinen bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
4719757fc8STomi Valkeinen 		const unsigned long __iomem *src, unsigned src_idx, int bits,
4819757fc8STomi Valkeinen 		unsigned n, u32 bswapmask)
4919757fc8STomi Valkeinen {
5019757fc8STomi Valkeinen 	unsigned long first, last;
5119757fc8STomi Valkeinen 	int const shift = dst_idx-src_idx;
5219757fc8STomi Valkeinen 
5319757fc8STomi Valkeinen #if 0
5419757fc8STomi Valkeinen 	/*
5519757fc8STomi Valkeinen 	 * If you suspect bug in this function, compare it with this simple
5619757fc8STomi Valkeinen 	 * memmove implementation.
5719757fc8STomi Valkeinen 	 */
585b789da8SMikulas Patocka 	memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
5919757fc8STomi Valkeinen 		(char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
6019757fc8STomi Valkeinen 	return;
6119757fc8STomi Valkeinen #endif
6219757fc8STomi Valkeinen 
6319757fc8STomi Valkeinen 	first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
6419757fc8STomi Valkeinen 	last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
6519757fc8STomi Valkeinen 
6619757fc8STomi Valkeinen 	if (!shift) {
6719757fc8STomi Valkeinen 		// Same alignment for source and dest
6819757fc8STomi Valkeinen 
6919757fc8STomi Valkeinen 		if (dst_idx+n <= bits) {
7019757fc8STomi Valkeinen 			// Single word
7119757fc8STomi Valkeinen 			if (last)
7219757fc8STomi Valkeinen 				first &= last;
7319757fc8STomi Valkeinen 			FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
7419757fc8STomi Valkeinen 		} else {
7519757fc8STomi Valkeinen 			// Multiple destination words
7619757fc8STomi Valkeinen 
7719757fc8STomi Valkeinen 			// Leading bits
7819757fc8STomi Valkeinen 			if (first != ~0UL) {
7919757fc8STomi Valkeinen 				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
8019757fc8STomi Valkeinen 				dst++;
8119757fc8STomi Valkeinen 				src++;
8219757fc8STomi Valkeinen 				n -= bits - dst_idx;
8319757fc8STomi Valkeinen 			}
8419757fc8STomi Valkeinen 
8519757fc8STomi Valkeinen 			// Main chunk
8619757fc8STomi Valkeinen 			n /= bits;
8719757fc8STomi Valkeinen 			while (n >= 8) {
8819757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src++), dst++);
8919757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src++), dst++);
9019757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src++), dst++);
9119757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src++), dst++);
9219757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src++), dst++);
9319757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src++), dst++);
9419757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src++), dst++);
9519757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src++), dst++);
9619757fc8STomi Valkeinen 				n -= 8;
9719757fc8STomi Valkeinen 			}
9819757fc8STomi Valkeinen 			while (n--)
9919757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src++), dst++);
10019757fc8STomi Valkeinen 
10119757fc8STomi Valkeinen 			// Trailing bits
10219757fc8STomi Valkeinen 			if (last)
10319757fc8STomi Valkeinen 				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
10419757fc8STomi Valkeinen 		}
10519757fc8STomi Valkeinen 	} else {
10619757fc8STomi Valkeinen 		/* Different alignment for source and dest */
10719757fc8STomi Valkeinen 		unsigned long d0, d1;
10819757fc8STomi Valkeinen 		int m;
10919757fc8STomi Valkeinen 
11019757fc8STomi Valkeinen 		int const left = shift & (bits - 1);
11119757fc8STomi Valkeinen 		int const right = -shift & (bits - 1);
11219757fc8STomi Valkeinen 
11319757fc8STomi Valkeinen 		if (dst_idx+n <= bits) {
11419757fc8STomi Valkeinen 			// Single destination word
11519757fc8STomi Valkeinen 			if (last)
11619757fc8STomi Valkeinen 				first &= last;
11719757fc8STomi Valkeinen 			d0 = FB_READL(src);
11819757fc8STomi Valkeinen 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
11919757fc8STomi Valkeinen 			if (shift > 0) {
12019757fc8STomi Valkeinen 				// Single source word
12119757fc8STomi Valkeinen 				d0 <<= left;
12219757fc8STomi Valkeinen 			} else if (src_idx+n <= bits) {
12319757fc8STomi Valkeinen 				// Single source word
12419757fc8STomi Valkeinen 				d0 >>= right;
12519757fc8STomi Valkeinen 			} else {
12619757fc8STomi Valkeinen 				// 2 source words
12719757fc8STomi Valkeinen 				d1 = FB_READL(src + 1);
12819757fc8STomi Valkeinen 				d1 = fb_rev_pixels_in_long(d1, bswapmask);
12919757fc8STomi Valkeinen 				d0 = d0 >> right | d1 << left;
13019757fc8STomi Valkeinen 			}
13119757fc8STomi Valkeinen 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
13219757fc8STomi Valkeinen 			FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
13319757fc8STomi Valkeinen 		} else {
13419757fc8STomi Valkeinen 			// Multiple destination words
13519757fc8STomi Valkeinen 			/** We must always remember the last value read, because in case
13619757fc8STomi Valkeinen 			SRC and DST overlap bitwise (e.g. when moving just one pixel in
13719757fc8STomi Valkeinen 			1bpp), we always collect one full long for DST and that might
13819757fc8STomi Valkeinen 			overlap with the current long from SRC. We store this value in
13919757fc8STomi Valkeinen 			'd0'. */
14019757fc8STomi Valkeinen 			d0 = FB_READL(src++);
14119757fc8STomi Valkeinen 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
14219757fc8STomi Valkeinen 			// Leading bits
14319757fc8STomi Valkeinen 			if (shift > 0) {
14419757fc8STomi Valkeinen 				// Single source word
14519757fc8STomi Valkeinen 				d1 = d0;
14619757fc8STomi Valkeinen 				d0 <<= left;
14719757fc8STomi Valkeinen 				n -= bits - dst_idx;
14819757fc8STomi Valkeinen 			} else {
14919757fc8STomi Valkeinen 				// 2 source words
15019757fc8STomi Valkeinen 				d1 = FB_READL(src++);
15119757fc8STomi Valkeinen 				d1 = fb_rev_pixels_in_long(d1, bswapmask);
15219757fc8STomi Valkeinen 
15319757fc8STomi Valkeinen 				d0 = d0 >> right | d1 << left;
15419757fc8STomi Valkeinen 				n -= bits - dst_idx;
15519757fc8STomi Valkeinen 			}
15619757fc8STomi Valkeinen 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
15719757fc8STomi Valkeinen 			FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
15819757fc8STomi Valkeinen 			d0 = d1;
15919757fc8STomi Valkeinen 			dst++;
16019757fc8STomi Valkeinen 
16119757fc8STomi Valkeinen 			// Main chunk
16219757fc8STomi Valkeinen 			m = n % bits;
16319757fc8STomi Valkeinen 			n /= bits;
16419757fc8STomi Valkeinen 			while ((n >= 4) && !bswapmask) {
16519757fc8STomi Valkeinen 				d1 = FB_READL(src++);
16619757fc8STomi Valkeinen 				FB_WRITEL(d0 >> right | d1 << left, dst++);
16719757fc8STomi Valkeinen 				d0 = d1;
16819757fc8STomi Valkeinen 				d1 = FB_READL(src++);
16919757fc8STomi Valkeinen 				FB_WRITEL(d0 >> right | d1 << left, dst++);
17019757fc8STomi Valkeinen 				d0 = d1;
17119757fc8STomi Valkeinen 				d1 = FB_READL(src++);
17219757fc8STomi Valkeinen 				FB_WRITEL(d0 >> right | d1 << left, dst++);
17319757fc8STomi Valkeinen 				d0 = d1;
17419757fc8STomi Valkeinen 				d1 = FB_READL(src++);
17519757fc8STomi Valkeinen 				FB_WRITEL(d0 >> right | d1 << left, dst++);
17619757fc8STomi Valkeinen 				d0 = d1;
17719757fc8STomi Valkeinen 				n -= 4;
17819757fc8STomi Valkeinen 			}
17919757fc8STomi Valkeinen 			while (n--) {
18019757fc8STomi Valkeinen 				d1 = FB_READL(src++);
18119757fc8STomi Valkeinen 				d1 = fb_rev_pixels_in_long(d1, bswapmask);
18219757fc8STomi Valkeinen 				d0 = d0 >> right | d1 << left;
18319757fc8STomi Valkeinen 				d0 = fb_rev_pixels_in_long(d0, bswapmask);
18419757fc8STomi Valkeinen 				FB_WRITEL(d0, dst++);
18519757fc8STomi Valkeinen 				d0 = d1;
18619757fc8STomi Valkeinen 			}
18719757fc8STomi Valkeinen 
18819757fc8STomi Valkeinen 			// Trailing bits
18919757fc8STomi Valkeinen 			if (m) {
19019757fc8STomi Valkeinen 				if (m <= bits - right) {
19119757fc8STomi Valkeinen 					// Single source word
19219757fc8STomi Valkeinen 					d0 >>= right;
19319757fc8STomi Valkeinen 				} else {
19419757fc8STomi Valkeinen 					// 2 source words
19519757fc8STomi Valkeinen 					d1 = FB_READL(src);
19619757fc8STomi Valkeinen 					d1 = fb_rev_pixels_in_long(d1,
19719757fc8STomi Valkeinen 								bswapmask);
19819757fc8STomi Valkeinen 					d0 = d0 >> right | d1 << left;
19919757fc8STomi Valkeinen 				}
20019757fc8STomi Valkeinen 				d0 = fb_rev_pixels_in_long(d0, bswapmask);
20119757fc8STomi Valkeinen 				FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
20219757fc8STomi Valkeinen 			}
20319757fc8STomi Valkeinen 		}
20419757fc8STomi Valkeinen 	}
20519757fc8STomi Valkeinen }
20619757fc8STomi Valkeinen 
20719757fc8STomi Valkeinen     /*
20819757fc8STomi Valkeinen      *  Generic bitwise copy algorithm, operating backward
20919757fc8STomi Valkeinen      */
21019757fc8STomi Valkeinen 
21119757fc8STomi Valkeinen static void
bitcpy_rev(struct fb_info * p,unsigned long __iomem * dst,unsigned dst_idx,const unsigned long __iomem * src,unsigned src_idx,int bits,unsigned n,u32 bswapmask)21219757fc8STomi Valkeinen bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
21319757fc8STomi Valkeinen 		const unsigned long __iomem *src, unsigned src_idx, int bits,
21419757fc8STomi Valkeinen 		unsigned n, u32 bswapmask)
21519757fc8STomi Valkeinen {
21619757fc8STomi Valkeinen 	unsigned long first, last;
21719757fc8STomi Valkeinen 	int shift;
21819757fc8STomi Valkeinen 
21919757fc8STomi Valkeinen #if 0
22019757fc8STomi Valkeinen 	/*
22119757fc8STomi Valkeinen 	 * If you suspect bug in this function, compare it with this simple
22219757fc8STomi Valkeinen 	 * memmove implementation.
22319757fc8STomi Valkeinen 	 */
2245b789da8SMikulas Patocka 	memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
22519757fc8STomi Valkeinen 		(char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
22619757fc8STomi Valkeinen 	return;
22719757fc8STomi Valkeinen #endif
22819757fc8STomi Valkeinen 
22919757fc8STomi Valkeinen 	dst += (dst_idx + n - 1) / bits;
23019757fc8STomi Valkeinen 	src += (src_idx + n - 1) / bits;
23119757fc8STomi Valkeinen 	dst_idx = (dst_idx + n - 1) % bits;
23219757fc8STomi Valkeinen 	src_idx = (src_idx + n - 1) % bits;
23319757fc8STomi Valkeinen 
23419757fc8STomi Valkeinen 	shift = dst_idx-src_idx;
23519757fc8STomi Valkeinen 
23619757fc8STomi Valkeinen 	first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask);
23719757fc8STomi Valkeinen 	last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask);
23819757fc8STomi Valkeinen 
23919757fc8STomi Valkeinen 	if (!shift) {
24019757fc8STomi Valkeinen 		// Same alignment for source and dest
24119757fc8STomi Valkeinen 
24219757fc8STomi Valkeinen 		if ((unsigned long)dst_idx+1 >= n) {
24319757fc8STomi Valkeinen 			// Single word
24419757fc8STomi Valkeinen 			if (first)
24519757fc8STomi Valkeinen 				last &= first;
24619757fc8STomi Valkeinen 			FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
24719757fc8STomi Valkeinen 		} else {
24819757fc8STomi Valkeinen 			// Multiple destination words
24919757fc8STomi Valkeinen 
25019757fc8STomi Valkeinen 			// Leading bits
25119757fc8STomi Valkeinen 			if (first) {
25219757fc8STomi Valkeinen 				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
25319757fc8STomi Valkeinen 				dst--;
25419757fc8STomi Valkeinen 				src--;
25519757fc8STomi Valkeinen 				n -= dst_idx+1;
25619757fc8STomi Valkeinen 			}
25719757fc8STomi Valkeinen 
25819757fc8STomi Valkeinen 			// Main chunk
25919757fc8STomi Valkeinen 			n /= bits;
26019757fc8STomi Valkeinen 			while (n >= 8) {
26119757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src--), dst--);
26219757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src--), dst--);
26319757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src--), dst--);
26419757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src--), dst--);
26519757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src--), dst--);
26619757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src--), dst--);
26719757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src--), dst--);
26819757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src--), dst--);
26919757fc8STomi Valkeinen 				n -= 8;
27019757fc8STomi Valkeinen 			}
27119757fc8STomi Valkeinen 			while (n--)
27219757fc8STomi Valkeinen 				FB_WRITEL(FB_READL(src--), dst--);
27319757fc8STomi Valkeinen 
27419757fc8STomi Valkeinen 			// Trailing bits
27519757fc8STomi Valkeinen 			if (last != -1UL)
27619757fc8STomi Valkeinen 				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
27719757fc8STomi Valkeinen 		}
27819757fc8STomi Valkeinen 	} else {
27919757fc8STomi Valkeinen 		// Different alignment for source and dest
28019757fc8STomi Valkeinen 		unsigned long d0, d1;
28119757fc8STomi Valkeinen 		int m;
28219757fc8STomi Valkeinen 
28319757fc8STomi Valkeinen 		int const left = shift & (bits-1);
28419757fc8STomi Valkeinen 		int const right = -shift & (bits-1);
28519757fc8STomi Valkeinen 
28619757fc8STomi Valkeinen 		if ((unsigned long)dst_idx+1 >= n) {
28719757fc8STomi Valkeinen 			// Single destination word
28819757fc8STomi Valkeinen 			if (first)
28919757fc8STomi Valkeinen 				last &= first;
29019757fc8STomi Valkeinen 			d0 = FB_READL(src);
29119757fc8STomi Valkeinen 			if (shift < 0) {
29219757fc8STomi Valkeinen 				// Single source word
29319757fc8STomi Valkeinen 				d0 >>= right;
29419757fc8STomi Valkeinen 			} else if (1+(unsigned long)src_idx >= n) {
29519757fc8STomi Valkeinen 				// Single source word
29619757fc8STomi Valkeinen 				d0 <<= left;
29719757fc8STomi Valkeinen 			} else {
29819757fc8STomi Valkeinen 				// 2 source words
29919757fc8STomi Valkeinen 				d1 = FB_READL(src - 1);
30019757fc8STomi Valkeinen 				d1 = fb_rev_pixels_in_long(d1, bswapmask);
30119757fc8STomi Valkeinen 				d0 = d0 << left | d1 >> right;
30219757fc8STomi Valkeinen 			}
30319757fc8STomi Valkeinen 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
30419757fc8STomi Valkeinen 			FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
30519757fc8STomi Valkeinen 		} else {
30619757fc8STomi Valkeinen 			// Multiple destination words
30719757fc8STomi Valkeinen 			/** We must always remember the last value read, because in case
30819757fc8STomi Valkeinen 			SRC and DST overlap bitwise (e.g. when moving just one pixel in
30919757fc8STomi Valkeinen 			1bpp), we always collect one full long for DST and that might
31019757fc8STomi Valkeinen 			overlap with the current long from SRC. We store this value in
31119757fc8STomi Valkeinen 			'd0'. */
31219757fc8STomi Valkeinen 
31319757fc8STomi Valkeinen 			d0 = FB_READL(src--);
31419757fc8STomi Valkeinen 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
31519757fc8STomi Valkeinen 			// Leading bits
31619757fc8STomi Valkeinen 			if (shift < 0) {
31719757fc8STomi Valkeinen 				// Single source word
31819757fc8STomi Valkeinen 				d1 = d0;
31919757fc8STomi Valkeinen 				d0 >>= right;
32019757fc8STomi Valkeinen 			} else {
32119757fc8STomi Valkeinen 				// 2 source words
32219757fc8STomi Valkeinen 				d1 = FB_READL(src--);
32319757fc8STomi Valkeinen 				d1 = fb_rev_pixels_in_long(d1, bswapmask);
32419757fc8STomi Valkeinen 				d0 = d0 << left | d1 >> right;
32519757fc8STomi Valkeinen 			}
32619757fc8STomi Valkeinen 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
3275b789da8SMikulas Patocka 			if (!first)
3285b789da8SMikulas Patocka 				FB_WRITEL(d0, dst);
3295b789da8SMikulas Patocka 			else
33019757fc8STomi Valkeinen 				FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
33119757fc8STomi Valkeinen 			d0 = d1;
33219757fc8STomi Valkeinen 			dst--;
33319757fc8STomi Valkeinen 			n -= dst_idx+1;
33419757fc8STomi Valkeinen 
33519757fc8STomi Valkeinen 			// Main chunk
33619757fc8STomi Valkeinen 			m = n % bits;
33719757fc8STomi Valkeinen 			n /= bits;
33819757fc8STomi Valkeinen 			while ((n >= 4) && !bswapmask) {
33919757fc8STomi Valkeinen 				d1 = FB_READL(src--);
34019757fc8STomi Valkeinen 				FB_WRITEL(d0 << left | d1 >> right, dst--);
34119757fc8STomi Valkeinen 				d0 = d1;
34219757fc8STomi Valkeinen 				d1 = FB_READL(src--);
34319757fc8STomi Valkeinen 				FB_WRITEL(d0 << left | d1 >> right, dst--);
34419757fc8STomi Valkeinen 				d0 = d1;
34519757fc8STomi Valkeinen 				d1 = FB_READL(src--);
34619757fc8STomi Valkeinen 				FB_WRITEL(d0 << left | d1 >> right, dst--);
34719757fc8STomi Valkeinen 				d0 = d1;
34819757fc8STomi Valkeinen 				d1 = FB_READL(src--);
34919757fc8STomi Valkeinen 				FB_WRITEL(d0 << left | d1 >> right, dst--);
35019757fc8STomi Valkeinen 				d0 = d1;
35119757fc8STomi Valkeinen 				n -= 4;
35219757fc8STomi Valkeinen 			}
35319757fc8STomi Valkeinen 			while (n--) {
35419757fc8STomi Valkeinen 				d1 = FB_READL(src--);
35519757fc8STomi Valkeinen 				d1 = fb_rev_pixels_in_long(d1, bswapmask);
35619757fc8STomi Valkeinen 				d0 = d0 << left | d1 >> right;
35719757fc8STomi Valkeinen 				d0 = fb_rev_pixels_in_long(d0, bswapmask);
35819757fc8STomi Valkeinen 				FB_WRITEL(d0, dst--);
35919757fc8STomi Valkeinen 				d0 = d1;
36019757fc8STomi Valkeinen 			}
36119757fc8STomi Valkeinen 
36219757fc8STomi Valkeinen 			// Trailing bits
36319757fc8STomi Valkeinen 			if (m) {
36419757fc8STomi Valkeinen 				if (m <= bits - left) {
36519757fc8STomi Valkeinen 					// Single source word
36619757fc8STomi Valkeinen 					d0 <<= left;
36719757fc8STomi Valkeinen 				} else {
36819757fc8STomi Valkeinen 					// 2 source words
36919757fc8STomi Valkeinen 					d1 = FB_READL(src);
37019757fc8STomi Valkeinen 					d1 = fb_rev_pixels_in_long(d1,
37119757fc8STomi Valkeinen 								bswapmask);
37219757fc8STomi Valkeinen 					d0 = d0 << left | d1 >> right;
37319757fc8STomi Valkeinen 				}
37419757fc8STomi Valkeinen 				d0 = fb_rev_pixels_in_long(d0, bswapmask);
37519757fc8STomi Valkeinen 				FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
37619757fc8STomi Valkeinen 			}
37719757fc8STomi Valkeinen 		}
37819757fc8STomi Valkeinen 	}
37919757fc8STomi Valkeinen }
38019757fc8STomi Valkeinen 
cfb_copyarea(struct fb_info * p,const struct fb_copyarea * area)38119757fc8STomi Valkeinen void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
38219757fc8STomi Valkeinen {
38319757fc8STomi Valkeinen 	u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
38419757fc8STomi Valkeinen 	u32 height = area->height, width = area->width;
385*7f33df94SSergey Shtylyov 	unsigned int const bits_per_line = p->fix.line_length * 8u;
38619757fc8STomi Valkeinen 	unsigned long __iomem *base = NULL;
38719757fc8STomi Valkeinen 	int bits = BITS_PER_LONG, bytes = bits >> 3;
38819757fc8STomi Valkeinen 	unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
38919757fc8STomi Valkeinen 	u32 bswapmask = fb_compute_bswapmask(p);
39019757fc8STomi Valkeinen 
39119757fc8STomi Valkeinen 	if (p->state != FBINFO_STATE_RUNNING)
39219757fc8STomi Valkeinen 		return;
39319757fc8STomi Valkeinen 
39419757fc8STomi Valkeinen 	/* if the beginning of the target area might overlap with the end of
39519757fc8STomi Valkeinen 	the source area, be have to copy the area reverse. */
39619757fc8STomi Valkeinen 	if ((dy == sy && dx > sx) || (dy > sy)) {
39719757fc8STomi Valkeinen 		dy += height;
39819757fc8STomi Valkeinen 		sy += height;
39919757fc8STomi Valkeinen 		rev_copy = 1;
40019757fc8STomi Valkeinen 	}
40119757fc8STomi Valkeinen 
40219757fc8STomi Valkeinen 	// split the base of the framebuffer into a long-aligned address and the
40319757fc8STomi Valkeinen 	// index of the first bit
40419757fc8STomi Valkeinen 	base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
40519757fc8STomi Valkeinen 	dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
40619757fc8STomi Valkeinen 	// add offset of source and target area
40719757fc8STomi Valkeinen 	dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
40819757fc8STomi Valkeinen 	src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
40919757fc8STomi Valkeinen 
41019757fc8STomi Valkeinen 	if (p->fbops->fb_sync)
41119757fc8STomi Valkeinen 		p->fbops->fb_sync(p);
41219757fc8STomi Valkeinen 
41319757fc8STomi Valkeinen 	if (rev_copy) {
41419757fc8STomi Valkeinen 		while (height--) {
41519757fc8STomi Valkeinen 			dst_idx -= bits_per_line;
41619757fc8STomi Valkeinen 			src_idx -= bits_per_line;
41719757fc8STomi Valkeinen 			bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
41819757fc8STomi Valkeinen 				base + (src_idx / bits), src_idx % bits, bits,
41919757fc8STomi Valkeinen 				width*p->var.bits_per_pixel, bswapmask);
42019757fc8STomi Valkeinen 		}
42119757fc8STomi Valkeinen 	} else {
42219757fc8STomi Valkeinen 		while (height--) {
42319757fc8STomi Valkeinen 			bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
42419757fc8STomi Valkeinen 				base + (src_idx / bits), src_idx % bits, bits,
42519757fc8STomi Valkeinen 				width*p->var.bits_per_pixel, bswapmask);
42619757fc8STomi Valkeinen 			dst_idx += bits_per_line;
42719757fc8STomi Valkeinen 			src_idx += bits_per_line;
42819757fc8STomi Valkeinen 		}
42919757fc8STomi Valkeinen 	}
43019757fc8STomi Valkeinen }
43119757fc8STomi Valkeinen 
43219757fc8STomi Valkeinen EXPORT_SYMBOL(cfb_copyarea);
43319757fc8STomi Valkeinen 
43419757fc8STomi Valkeinen MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
43519757fc8STomi Valkeinen MODULE_DESCRIPTION("Generic software accelerated copyarea");
43619757fc8STomi Valkeinen MODULE_LICENSE("GPL");
43719757fc8STomi Valkeinen 
438