2 * Generic function for frame buffer with packed pixels of any depth.
4 * Copyright (C) 1999-2005 James Simmons <jsimmons@www.infradead.org>
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
12 * This is for cfb packed pixels. Iplan and such are incorporated in the
13 * drivers that need them.
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.
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.
27 #include <linux/module.h>
28 #include <linux/kernel.h>
29 #include <linux/string.h>
31 #include <linux/slab.h>
32 #include <asm/types.h>
35 #if BITS_PER_LONG == 32
36 # define FB_WRITEL fb_writel
37 # define FB_READL fb_readl
39 # define FB_WRITEL fb_writeq
40 # define FB_READL fb_readq
44 * Compose two values, using a bitmask as decision value
45 * This is equivalent to (a & mask) | (b & ~mask)
48 static inline unsigned long
49 comp(unsigned long a
, unsigned long b
, unsigned long mask
)
51 return ((a
^ b
) & mask
) ^ b
;
55 * Generic bitwise copy algorithm
59 bitcpy(unsigned long __iomem
*dst
, int dst_idx
, const unsigned long __iomem
*src
,
60 int src_idx
, int bits
, unsigned n
)
62 unsigned long first
, last
;
63 int const shift
= dst_idx
-src_idx
;
66 first
= FB_SHIFT_HIGH(~0UL, dst_idx
);
67 last
= ~(FB_SHIFT_HIGH(~0UL, (dst_idx
+n
) % bits
));
70 // Same alignment for source and dest
72 if (dst_idx
+n
<= bits
) {
76 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
78 // Multiple destination words
82 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), 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 FB_WRITEL(FB_READL(src
++), dst
++);
97 FB_WRITEL(FB_READL(src
++), dst
++);
98 FB_WRITEL(FB_READL(src
++), dst
++);
102 FB_WRITEL(FB_READL(src
++), dst
++);
106 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), last
), dst
);
109 unsigned long d0
, d1
;
111 // Different alignment for source and dest
113 right
= shift
& (bits
- 1);
114 left
= -shift
& (bits
- 1);
116 if (dst_idx
+n
<= bits
) {
117 // Single destination word
121 // Single source word
122 FB_WRITEL( comp( FB_READL(src
) >> right
, FB_READL(dst
), first
), dst
);
123 } else if (src_idx
+n
<= bits
) {
124 // Single source word
125 FB_WRITEL( comp(FB_READL(src
) << left
, FB_READL(dst
), first
), dst
);
128 d0
= FB_READL(src
++);
130 FB_WRITEL( comp(d0
<<left
| d1
>>right
, FB_READL(dst
), first
), dst
);
133 // Multiple destination words
134 /** We must always remember the last value read, because in case
135 SRC and DST overlap bitwise (e.g. when moving just one pixel in
136 1bpp), we always collect one full long for DST and that might
137 overlap with the current long from SRC. We store this value in
139 d0
= FB_READL(src
++);
142 // Single source word
143 FB_WRITEL( comp(d0
>> right
, FB_READL(dst
), first
), dst
);
148 d1
= FB_READL(src
++);
149 FB_WRITEL( comp(d0
<<left
| d1
>>right
, FB_READL(dst
), first
), dst
);
159 d1
= FB_READL(src
++);
160 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
162 d1
= FB_READL(src
++);
163 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
165 d1
= FB_READL(src
++);
166 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
168 d1
= FB_READL(src
++);
169 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
174 d1
= FB_READL(src
++);
175 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
182 // Single source word
183 FB_WRITEL( comp(d0
<< left
, FB_READL(dst
), last
), dst
);
187 FB_WRITEL( comp(d0
<<left
| d1
>>right
, FB_READL(dst
), last
), dst
);
195 * Generic bitwise copy algorithm, operating backward
199 bitcpy_rev(unsigned long __iomem
*dst
, int dst_idx
, const unsigned long __iomem
*src
,
200 int src_idx
, int bits
, unsigned n
)
202 unsigned long first
, last
;
208 dst_idx
+= (n
-1) % bits
;
209 dst
+= dst_idx
>> (ffs(bits
) - 1);
211 src_idx
+= (n
-1) % bits
;
212 src
+= src_idx
>> (ffs(bits
) - 1);
216 shift
= dst_idx
-src_idx
;
218 first
= FB_SHIFT_LOW(~0UL, bits
- 1 - dst_idx
);
219 last
= ~(FB_SHIFT_LOW(~0UL, bits
- 1 - ((dst_idx
-n
) % bits
)));
222 // Same alignment for source and dest
224 if ((unsigned long)dst_idx
+1 >= n
) {
228 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
230 // Multiple destination words
234 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
243 FB_WRITEL(FB_READL(src
--), dst
--);
244 FB_WRITEL(FB_READL(src
--), dst
--);
245 FB_WRITEL(FB_READL(src
--), dst
--);
246 FB_WRITEL(FB_READL(src
--), dst
--);
247 FB_WRITEL(FB_READL(src
--), dst
--);
248 FB_WRITEL(FB_READL(src
--), dst
--);
249 FB_WRITEL(FB_READL(src
--), dst
--);
250 FB_WRITEL(FB_READL(src
--), dst
--);
254 FB_WRITEL(FB_READL(src
--), dst
--);
258 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), last
), dst
);
261 // Different alignment for source and dest
263 int const left
= -shift
& (bits
-1);
264 int const right
= shift
& (bits
-1);
266 if ((unsigned long)dst_idx
+1 >= n
) {
267 // Single destination word
271 // Single source word
272 FB_WRITEL( comp( FB_READL(src
)<<left
, FB_READL(dst
), first
), dst
);
273 } else if (1+(unsigned long)src_idx
>= n
) {
274 // Single source word
275 FB_WRITEL( comp( FB_READL(src
)>>right
, FB_READL(dst
), first
), dst
);
278 FB_WRITEL( comp( (FB_READL(src
)>>right
| FB_READL(src
-1)<<left
), FB_READL(dst
), first
), dst
);
281 // Multiple destination words
282 /** We must always remember the last value read, because in case
283 SRC and DST overlap bitwise (e.g. when moving just one pixel in
284 1bpp), we always collect one full long for DST and that might
285 overlap with the current long from SRC. We store this value in
287 unsigned long d0
, d1
;
290 d0
= FB_READL(src
--);
293 // Single source word
294 FB_WRITEL( comp( (d0
<< left
), FB_READL(dst
), first
), dst
);
297 d1
= FB_READL(src
--);
298 FB_WRITEL( comp( (d0
>>right
| d1
<<left
), FB_READL(dst
), first
), dst
);
308 d1
= FB_READL(src
--);
309 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
311 d1
= FB_READL(src
--);
312 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
314 d1
= FB_READL(src
--);
315 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
317 d1
= FB_READL(src
--);
318 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
323 d1
= FB_READL(src
--);
324 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
331 // Single source word
332 FB_WRITEL( comp(d0
>> right
, FB_READL(dst
), last
), dst
);
336 FB_WRITEL( comp(d0
>>right
| d1
<<left
, FB_READL(dst
), last
), dst
);
343 void cfb_copyarea(struct fb_info
*p
, const struct fb_copyarea
*area
)
345 u32 dx
= area
->dx
, dy
= area
->dy
, sx
= area
->sx
, sy
= area
->sy
;
346 u32 height
= area
->height
, width
= area
->width
;
347 unsigned long const bits_per_line
= p
->fix
.line_length
*8u;
348 unsigned long __iomem
*dst
= NULL
, *src
= NULL
;
349 int bits
= BITS_PER_LONG
, bytes
= bits
>> 3;
350 int dst_idx
= 0, src_idx
= 0, rev_copy
= 0;
352 if (p
->state
!= FBINFO_STATE_RUNNING
)
355 /* if the beginning of the target area might overlap with the end of
356 the source area, be have to copy the area reverse. */
357 if ((dy
== sy
&& dx
> sx
) || (dy
> sy
)) {
363 // split the base of the framebuffer into a long-aligned address and the
364 // index of the first bit
365 dst
= src
= (unsigned long __iomem
*)((unsigned long)p
->screen_base
& ~(bytes
-1));
366 dst_idx
= src_idx
= 8*((unsigned long)p
->screen_base
& (bytes
-1));
367 // add offset of source and target area
368 dst_idx
+= dy
*bits_per_line
+ dx
*p
->var
.bits_per_pixel
;
369 src_idx
+= sy
*bits_per_line
+ sx
*p
->var
.bits_per_pixel
;
371 if (p
->fbops
->fb_sync
)
372 p
->fbops
->fb_sync(p
);
376 dst_idx
-= bits_per_line
;
377 src_idx
-= bits_per_line
;
378 dst
+= dst_idx
>> (ffs(bits
) - 1);
379 dst_idx
&= (bytes
- 1);
380 src
+= src_idx
>> (ffs(bits
) - 1);
381 src_idx
&= (bytes
- 1);
382 bitcpy_rev(dst
, dst_idx
, src
, src_idx
, bits
,
383 width
*p
->var
.bits_per_pixel
);
387 dst
+= dst_idx
>> (ffs(bits
) - 1);
388 dst_idx
&= (bytes
- 1);
389 src
+= src_idx
>> (ffs(bits
) - 1);
390 src_idx
&= (bytes
- 1);
391 bitcpy(dst
, dst_idx
, src
, src_idx
, bits
,
392 width
*p
->var
.bits_per_pixel
);
393 dst_idx
+= bits_per_line
;
394 src_idx
+= bits_per_line
;
399 EXPORT_SYMBOL(cfb_copyarea
);
401 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
402 MODULE_DESCRIPTION("Generic software accelerated copyarea");
403 MODULE_LICENSE("GPL");