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 <linux/slab.h>
30 #include <asm/types.h>
34 #if BITS_PER_LONG == 32
35 # define FB_WRITEL fb_writel
36 # define FB_READL fb_readl
38 # define FB_WRITEL fb_writeq
39 # define FB_READL fb_readq
43 * Generic bitwise copy algorithm
47 bitcpy(unsigned long __iomem
*dst
, int dst_idx
, const unsigned long __iomem
*src
,
48 int src_idx
, int bits
, unsigned n
, u32 bswapmask
)
50 unsigned long first
, last
;
51 int const shift
= dst_idx
-src_idx
;
54 first
= fb_shifted_pixels_mask_long(dst_idx
, bswapmask
);
55 last
= ~fb_shifted_pixels_mask_long((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(unsigned long __iomem
*dst
, int dst_idx
, const unsigned long __iomem
*src
,
206 int src_idx
, int bits
, unsigned n
, u32 bswapmask
)
208 unsigned long first
, last
;
214 dst_idx
+= (n
-1) % bits
;
215 dst
+= dst_idx
>> (ffs(bits
) - 1);
217 src_idx
+= (n
-1) % bits
;
218 src
+= src_idx
>> (ffs(bits
) - 1);
222 shift
= dst_idx
-src_idx
;
224 first
= fb_shifted_pixels_mask_long(bits
- 1 - dst_idx
, bswapmask
);
225 last
= ~fb_shifted_pixels_mask_long(bits
- 1 - ((dst_idx
-n
) % bits
), bswapmask
);
228 // Same alignment for source and dest
230 if ((unsigned long)dst_idx
+1 >= n
) {
234 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
236 // Multiple destination words
240 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
249 FB_WRITEL(FB_READL(src
--), dst
--);
250 FB_WRITEL(FB_READL(src
--), 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
--);
260 FB_WRITEL(FB_READL(src
--), dst
--);
264 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), last
), dst
);
267 // Different alignment for source and dest
268 unsigned long d0
, d1
;
271 int const left
= -shift
& (bits
-1);
272 int const right
= shift
& (bits
-1);
275 if ((unsigned long)dst_idx
+1 >= n
) {
276 // Single destination word
281 // Single source word
283 } else if (1+(unsigned long)src_idx
>= n
) {
284 // Single source word
288 d1
= FB_READL(src
- 1);
289 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
290 d0
= d0
>>right
| d1
<<left
;
292 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
293 FB_WRITEL(comp(d0
, FB_READL(dst
), first
), dst
);
295 // Multiple destination words
296 /** We must always remember the last value read, because in case
297 SRC and DST overlap bitwise (e.g. when moving just one pixel in
298 1bpp), we always collect one full long for DST and that might
299 overlap with the current long from SRC. We store this value in
302 d0
= FB_READL(src
--);
303 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
306 // Single source word
311 d1
= FB_READL(src
--);
312 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
313 d0
= d0
>>right
| d1
<<left
;
315 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
316 FB_WRITEL(comp(d0
, FB_READL(dst
), first
), dst
);
324 while ((n
>= 4) && !bswapmask
) {
325 d1
= FB_READL(src
--);
326 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
328 d1
= FB_READL(src
--);
329 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
331 d1
= FB_READL(src
--);
332 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
334 d1
= FB_READL(src
--);
335 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
340 d1
= FB_READL(src
--);
341 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
342 d0
= d0
>> right
| d1
<< left
;
343 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
344 FB_WRITEL(d0
, dst
--);
351 // Single source word
356 d1
= fb_rev_pixels_in_long(d1
,
358 d0
= d0
>>right
| d1
<<left
;
360 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
361 FB_WRITEL(comp(d0
, FB_READL(dst
), last
), dst
);
367 void cfb_copyarea(struct fb_info
*p
, const struct fb_copyarea
*area
)
369 u32 dx
= area
->dx
, dy
= area
->dy
, sx
= area
->sx
, sy
= area
->sy
;
370 u32 height
= area
->height
, width
= area
->width
;
371 unsigned long const bits_per_line
= p
->fix
.line_length
*8u;
372 unsigned long __iomem
*dst
= NULL
, *src
= NULL
;
373 int bits
= BITS_PER_LONG
, bytes
= bits
>> 3;
374 int dst_idx
= 0, src_idx
= 0, rev_copy
= 0;
375 u32 bswapmask
= fb_compute_bswapmask(p
);
377 if (p
->state
!= FBINFO_STATE_RUNNING
)
380 /* if the beginning of the target area might overlap with the end of
381 the source area, be have to copy the area reverse. */
382 if ((dy
== sy
&& dx
> sx
) || (dy
> sy
)) {
388 // split the base of the framebuffer into a long-aligned address and the
389 // index of the first bit
390 dst
= src
= (unsigned long __iomem
*)((unsigned long)p
->screen_base
& ~(bytes
-1));
391 dst_idx
= src_idx
= 8*((unsigned long)p
->screen_base
& (bytes
-1));
392 // add offset of source and target area
393 dst_idx
+= dy
*bits_per_line
+ dx
*p
->var
.bits_per_pixel
;
394 src_idx
+= sy
*bits_per_line
+ sx
*p
->var
.bits_per_pixel
;
396 if (p
->fbops
->fb_sync
)
397 p
->fbops
->fb_sync(p
);
401 dst_idx
-= bits_per_line
;
402 src_idx
-= bits_per_line
;
403 dst
+= dst_idx
>> (ffs(bits
) - 1);
404 dst_idx
&= (bytes
- 1);
405 src
+= src_idx
>> (ffs(bits
) - 1);
406 src_idx
&= (bytes
- 1);
407 bitcpy_rev(dst
, dst_idx
, src
, src_idx
, bits
,
408 width
*p
->var
.bits_per_pixel
, bswapmask
);
412 dst
+= dst_idx
>> (ffs(bits
) - 1);
413 dst_idx
&= (bytes
- 1);
414 src
+= src_idx
>> (ffs(bits
) - 1);
415 src_idx
&= (bytes
- 1);
416 bitcpy(dst
, dst_idx
, src
, src_idx
, bits
,
417 width
*p
->var
.bits_per_pixel
, bswapmask
);
418 dst_idx
+= bits_per_line
;
419 src_idx
+= bits_per_line
;
424 EXPORT_SYMBOL(cfb_copyarea
);
426 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
427 MODULE_DESCRIPTION("Generic software accelerated copyarea");
428 MODULE_LICENSE("GPL");