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.
25 #include <linux/module.h>
26 #include <linux/kernel.h>
27 #include <linux/string.h>
29 #include <asm/types.h>
33 #if BITS_PER_LONG == 32
34 # define FB_WRITEL fb_writel
35 # define FB_READL fb_readl
37 # define FB_WRITEL fb_writeq
38 # define FB_READL fb_readq
42 * Generic bitwise copy algorithm
46 bitcpy(struct fb_info
*p
, unsigned long __iomem
*dst
, int dst_idx
,
47 const unsigned long __iomem
*src
, int src_idx
, int bits
,
48 unsigned n
, u32 bswapmask
)
50 unsigned long first
, last
;
51 int const shift
= dst_idx
-src_idx
;
54 first
= fb_shifted_pixels_mask_long(p
, dst_idx
, bswapmask
);
55 last
= ~fb_shifted_pixels_mask_long(p
, (dst_idx
+n
) % bits
, bswapmask
);
58 // Same alignment for source and dest
60 if (dst_idx
+n
<= bits
) {
64 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
66 // Multiple destination words
70 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
79 FB_WRITEL(FB_READL(src
++), dst
++);
80 FB_WRITEL(FB_READL(src
++), dst
++);
81 FB_WRITEL(FB_READL(src
++), dst
++);
82 FB_WRITEL(FB_READL(src
++), dst
++);
83 FB_WRITEL(FB_READL(src
++), dst
++);
84 FB_WRITEL(FB_READL(src
++), dst
++);
85 FB_WRITEL(FB_READL(src
++), dst
++);
86 FB_WRITEL(FB_READL(src
++), dst
++);
90 FB_WRITEL(FB_READL(src
++), dst
++);
94 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), last
), dst
);
97 /* Different alignment for source and dest */
101 right
= shift
& (bits
- 1);
102 left
= -shift
& (bits
- 1);
105 if (dst_idx
+n
<= bits
) {
106 // Single destination word
110 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
112 // Single source word
114 } else if (src_idx
+n
<= bits
) {
115 // Single source word
119 d1
= FB_READL(src
+ 1);
120 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
121 d0
= d0
<<left
| d1
>>right
;
123 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
124 FB_WRITEL(comp(d0
, FB_READL(dst
), first
), dst
);
126 // Multiple destination words
127 /** We must always remember the last value read, because in case
128 SRC and DST overlap bitwise (e.g. when moving just one pixel in
129 1bpp), we always collect one full long for DST and that might
130 overlap with the current long from SRC. We store this value in
132 d0
= FB_READL(src
++);
133 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
136 // Single source word
143 d1
= FB_READL(src
++);
144 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
146 d0
= d0
<<left
| d1
>>right
;
150 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
151 FB_WRITEL(comp(d0
, FB_READL(dst
), first
), dst
);
157 while ((n
>= 4) && !bswapmask
) {
158 d1
= FB_READL(src
++);
159 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
161 d1
= FB_READL(src
++);
162 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
164 d1
= FB_READL(src
++);
165 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
167 d1
= FB_READL(src
++);
168 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
173 d1
= FB_READL(src
++);
174 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
175 d0
= d0
<< left
| d1
>> right
;
176 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
177 FB_WRITEL(d0
, dst
++);
184 // Single source word
189 d1
= fb_rev_pixels_in_long(d1
,
191 d0
= d0
<<left
| d1
>>right
;
193 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
194 FB_WRITEL(comp(d0
, FB_READL(dst
), last
), dst
);
201 * Generic bitwise copy algorithm, operating backward
205 bitcpy_rev(struct fb_info
*p
, unsigned long __iomem
*dst
, int dst_idx
,
206 const unsigned long __iomem
*src
, int src_idx
, int bits
,
207 unsigned n
, u32 bswapmask
)
209 unsigned long first
, last
;
215 dst_idx
+= (n
-1) % bits
;
216 dst
+= dst_idx
>> (ffs(bits
) - 1);
218 src_idx
+= (n
-1) % bits
;
219 src
+= src_idx
>> (ffs(bits
) - 1);
223 shift
= dst_idx
-src_idx
;
225 first
= fb_shifted_pixels_mask_long(p
, bits
- 1 - dst_idx
, bswapmask
);
226 last
= ~fb_shifted_pixels_mask_long(p
, bits
- 1 - ((dst_idx
-n
) % bits
),
230 // Same alignment for source and dest
232 if ((unsigned long)dst_idx
+1 >= n
) {
236 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
238 // Multiple destination words
242 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
251 FB_WRITEL(FB_READL(src
--), dst
--);
252 FB_WRITEL(FB_READL(src
--), dst
--);
253 FB_WRITEL(FB_READL(src
--), dst
--);
254 FB_WRITEL(FB_READL(src
--), dst
--);
255 FB_WRITEL(FB_READL(src
--), dst
--);
256 FB_WRITEL(FB_READL(src
--), dst
--);
257 FB_WRITEL(FB_READL(src
--), dst
--);
258 FB_WRITEL(FB_READL(src
--), dst
--);
262 FB_WRITEL(FB_READL(src
--), dst
--);
266 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), last
), dst
);
269 // Different alignment for source and dest
270 unsigned long d0
, d1
;
273 int const left
= -shift
& (bits
-1);
274 int const right
= shift
& (bits
-1);
277 if ((unsigned long)dst_idx
+1 >= n
) {
278 // Single destination word
283 // Single source word
285 } else if (1+(unsigned long)src_idx
>= n
) {
286 // Single source word
290 d1
= FB_READL(src
- 1);
291 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
292 d0
= d0
>>right
| d1
<<left
;
294 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
295 FB_WRITEL(comp(d0
, FB_READL(dst
), first
), dst
);
297 // Multiple destination words
298 /** We must always remember the last value read, because in case
299 SRC and DST overlap bitwise (e.g. when moving just one pixel in
300 1bpp), we always collect one full long for DST and that might
301 overlap with the current long from SRC. We store this value in
304 d0
= FB_READL(src
--);
305 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
308 // Single source word
313 d1
= FB_READL(src
--);
314 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
315 d0
= d0
>>right
| d1
<<left
;
317 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
318 FB_WRITEL(comp(d0
, FB_READL(dst
), first
), dst
);
326 while ((n
>= 4) && !bswapmask
) {
327 d1
= FB_READL(src
--);
328 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
330 d1
= FB_READL(src
--);
331 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
333 d1
= FB_READL(src
--);
334 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
336 d1
= FB_READL(src
--);
337 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
342 d1
= FB_READL(src
--);
343 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
344 d0
= d0
>> right
| d1
<< left
;
345 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
346 FB_WRITEL(d0
, dst
--);
353 // Single source word
358 d1
= fb_rev_pixels_in_long(d1
,
360 d0
= d0
>>right
| d1
<<left
;
362 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
363 FB_WRITEL(comp(d0
, FB_READL(dst
), last
), dst
);
369 void cfb_copyarea(struct fb_info
*p
, const struct fb_copyarea
*area
)
371 u32 dx
= area
->dx
, dy
= area
->dy
, sx
= area
->sx
, sy
= area
->sy
;
372 u32 height
= area
->height
, width
= area
->width
;
373 unsigned long const bits_per_line
= p
->fix
.line_length
*8u;
374 unsigned long __iomem
*dst
= NULL
, *src
= NULL
;
375 int bits
= BITS_PER_LONG
, bytes
= bits
>> 3;
376 int dst_idx
= 0, src_idx
= 0, rev_copy
= 0;
377 u32 bswapmask
= fb_compute_bswapmask(p
);
379 if (p
->state
!= FBINFO_STATE_RUNNING
)
382 /* if the beginning of the target area might overlap with the end of
383 the source area, be have to copy the area reverse. */
384 if ((dy
== sy
&& dx
> sx
) || (dy
> sy
)) {
390 // split the base of the framebuffer into a long-aligned address and the
391 // index of the first bit
392 dst
= src
= (unsigned long __iomem
*)((unsigned long)p
->screen_base
& ~(bytes
-1));
393 dst_idx
= src_idx
= 8*((unsigned long)p
->screen_base
& (bytes
-1));
394 // add offset of source and target area
395 dst_idx
+= dy
*bits_per_line
+ dx
*p
->var
.bits_per_pixel
;
396 src_idx
+= sy
*bits_per_line
+ sx
*p
->var
.bits_per_pixel
;
398 if (p
->fbops
->fb_sync
)
399 p
->fbops
->fb_sync(p
);
403 dst_idx
-= bits_per_line
;
404 src_idx
-= bits_per_line
;
405 dst
+= dst_idx
>> (ffs(bits
) - 1);
406 dst_idx
&= (bytes
- 1);
407 src
+= src_idx
>> (ffs(bits
) - 1);
408 src_idx
&= (bytes
- 1);
409 bitcpy_rev(p
, dst
, dst_idx
, src
, src_idx
, bits
,
410 width
*p
->var
.bits_per_pixel
, bswapmask
);
414 dst
+= dst_idx
>> (ffs(bits
) - 1);
415 dst_idx
&= (bytes
- 1);
416 src
+= src_idx
>> (ffs(bits
) - 1);
417 src_idx
&= (bytes
- 1);
418 bitcpy(p
, dst
, dst_idx
, src
, src_idx
, bits
,
419 width
*p
->var
.bits_per_pixel
, bswapmask
);
420 dst_idx
+= bits_per_line
;
421 src_idx
+= bits_per_line
;
426 EXPORT_SYMBOL(cfb_copyarea
);
428 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
429 MODULE_DESCRIPTION("Generic software accelerated copyarea");
430 MODULE_LICENSE("GPL");