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
, unsigned dst_idx
,
47 const unsigned long __iomem
*src
, unsigned src_idx
, int bits
,
48 unsigned n
, u32 bswapmask
)
50 unsigned long first
, last
;
51 int const shift
= dst_idx
-src_idx
;
55 * If you suspect bug in this function, compare it with this simple
56 * memmove implementation.
58 memmove((char *)dst
+ ((dst_idx
& (bits
- 1))) / 8,
59 (char *)src
+ ((src_idx
& (bits
- 1))) / 8, n
/ 8);
63 first
= fb_shifted_pixels_mask_long(p
, dst_idx
, bswapmask
);
64 last
= ~fb_shifted_pixels_mask_long(p
, (dst_idx
+n
) % bits
, bswapmask
);
67 // Same alignment for source and dest
69 if (dst_idx
+n
<= bits
) {
73 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
75 // Multiple destination words
79 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
88 FB_WRITEL(FB_READL(src
++), dst
++);
89 FB_WRITEL(FB_READL(src
++), dst
++);
90 FB_WRITEL(FB_READL(src
++), 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
++);
99 FB_WRITEL(FB_READL(src
++), dst
++);
103 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), last
), dst
);
106 /* Different alignment for source and dest */
107 unsigned long d0
, d1
;
110 int const left
= shift
& (bits
- 1);
111 int const right
= -shift
& (bits
- 1);
113 if (dst_idx
+n
<= bits
) {
114 // Single destination word
118 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
120 // Single source word
122 } else if (src_idx
+n
<= bits
) {
123 // Single source word
127 d1
= FB_READL(src
+ 1);
128 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
129 d0
= d0
>> right
| d1
<< left
;
131 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
132 FB_WRITEL(comp(d0
, FB_READL(dst
), first
), dst
);
134 // Multiple destination words
135 /** We must always remember the last value read, because in case
136 SRC and DST overlap bitwise (e.g. when moving just one pixel in
137 1bpp), we always collect one full long for DST and that might
138 overlap with the current long from SRC. We store this value in
140 d0
= FB_READL(src
++);
141 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
144 // Single source word
150 d1
= FB_READL(src
++);
151 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
153 d0
= d0
>> right
| d1
<< left
;
156 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
157 FB_WRITEL(comp(d0
, FB_READL(dst
), first
), dst
);
164 while ((n
>= 4) && !bswapmask
) {
165 d1
= FB_READL(src
++);
166 FB_WRITEL(d0
>> right
| d1
<< left
, dst
++);
168 d1
= FB_READL(src
++);
169 FB_WRITEL(d0
>> right
| d1
<< left
, dst
++);
171 d1
= FB_READL(src
++);
172 FB_WRITEL(d0
>> right
| d1
<< left
, dst
++);
174 d1
= FB_READL(src
++);
175 FB_WRITEL(d0
>> right
| d1
<< left
, dst
++);
180 d1
= FB_READL(src
++);
181 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
182 d0
= d0
>> right
| d1
<< left
;
183 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
184 FB_WRITEL(d0
, dst
++);
190 if (m
<= bits
- right
) {
191 // Single source word
196 d1
= fb_rev_pixels_in_long(d1
,
198 d0
= d0
>> right
| d1
<< left
;
200 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
201 FB_WRITEL(comp(d0
, FB_READL(dst
), last
), dst
);
208 * Generic bitwise copy algorithm, operating backward
212 bitcpy_rev(struct fb_info
*p
, unsigned long __iomem
*dst
, unsigned dst_idx
,
213 const unsigned long __iomem
*src
, unsigned src_idx
, int bits
,
214 unsigned n
, u32 bswapmask
)
216 unsigned long first
, last
;
221 * If you suspect bug in this function, compare it with this simple
222 * memmove implementation.
224 memmove((char *)dst
+ ((dst_idx
& (bits
- 1))) / 8,
225 (char *)src
+ ((src_idx
& (bits
- 1))) / 8, n
/ 8);
229 dst
+= (dst_idx
+ n
- 1) / bits
;
230 src
+= (src_idx
+ n
- 1) / bits
;
231 dst_idx
= (dst_idx
+ n
- 1) % bits
;
232 src_idx
= (src_idx
+ n
- 1) % bits
;
234 shift
= dst_idx
-src_idx
;
236 first
= ~fb_shifted_pixels_mask_long(p
, (dst_idx
+ 1) % bits
, bswapmask
);
237 last
= fb_shifted_pixels_mask_long(p
, (bits
+ dst_idx
+ 1 - n
) % bits
, bswapmask
);
240 // Same alignment for source and dest
242 if ((unsigned long)dst_idx
+1 >= n
) {
246 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), last
), dst
);
248 // Multiple destination words
252 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
261 FB_WRITEL(FB_READL(src
--), dst
--);
262 FB_WRITEL(FB_READL(src
--), dst
--);
263 FB_WRITEL(FB_READL(src
--), dst
--);
264 FB_WRITEL(FB_READL(src
--), dst
--);
265 FB_WRITEL(FB_READL(src
--), dst
--);
266 FB_WRITEL(FB_READL(src
--), dst
--);
267 FB_WRITEL(FB_READL(src
--), dst
--);
268 FB_WRITEL(FB_READL(src
--), dst
--);
272 FB_WRITEL(FB_READL(src
--), dst
--);
276 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), last
), dst
);
279 // Different alignment for source and dest
280 unsigned long d0
, d1
;
283 int const left
= shift
& (bits
-1);
284 int const right
= -shift
& (bits
-1);
286 if ((unsigned long)dst_idx
+1 >= n
) {
287 // Single destination word
292 // Single source word
294 } else if (1+(unsigned long)src_idx
>= n
) {
295 // Single source word
299 d1
= FB_READL(src
- 1);
300 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
301 d0
= d0
<< left
| d1
>> right
;
303 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
304 FB_WRITEL(comp(d0
, FB_READL(dst
), last
), dst
);
306 // Multiple destination words
307 /** We must always remember the last value read, because in case
308 SRC and DST overlap bitwise (e.g. when moving just one pixel in
309 1bpp), we always collect one full long for DST and that might
310 overlap with the current long from SRC. We store this value in
313 d0
= FB_READL(src
--);
314 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
317 // Single source word
322 d1
= FB_READL(src
--);
323 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
324 d0
= d0
<< left
| d1
>> right
;
326 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
330 FB_WRITEL(comp(d0
, FB_READL(dst
), first
), dst
);
338 while ((n
>= 4) && !bswapmask
) {
339 d1
= FB_READL(src
--);
340 FB_WRITEL(d0
<< left
| d1
>> right
, dst
--);
342 d1
= FB_READL(src
--);
343 FB_WRITEL(d0
<< left
| d1
>> right
, dst
--);
345 d1
= FB_READL(src
--);
346 FB_WRITEL(d0
<< left
| d1
>> right
, dst
--);
348 d1
= FB_READL(src
--);
349 FB_WRITEL(d0
<< left
| d1
>> right
, dst
--);
354 d1
= FB_READL(src
--);
355 d1
= fb_rev_pixels_in_long(d1
, bswapmask
);
356 d0
= d0
<< left
| d1
>> right
;
357 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
358 FB_WRITEL(d0
, dst
--);
364 if (m
<= bits
- left
) {
365 // Single source word
370 d1
= fb_rev_pixels_in_long(d1
,
372 d0
= d0
<< left
| d1
>> right
;
374 d0
= fb_rev_pixels_in_long(d0
, bswapmask
);
375 FB_WRITEL(comp(d0
, FB_READL(dst
), last
), dst
);
381 void cfb_copyarea(struct fb_info
*p
, const struct fb_copyarea
*area
)
383 u32 dx
= area
->dx
, dy
= area
->dy
, sx
= area
->sx
, sy
= area
->sy
;
384 u32 height
= area
->height
, width
= area
->width
;
385 unsigned long const bits_per_line
= p
->fix
.line_length
*8u;
386 unsigned long __iomem
*base
= NULL
;
387 int bits
= BITS_PER_LONG
, bytes
= bits
>> 3;
388 unsigned dst_idx
= 0, src_idx
= 0, rev_copy
= 0;
389 u32 bswapmask
= fb_compute_bswapmask(p
);
391 if (p
->state
!= FBINFO_STATE_RUNNING
)
394 /* if the beginning of the target area might overlap with the end of
395 the source area, be have to copy the area reverse. */
396 if ((dy
== sy
&& dx
> sx
) || (dy
> sy
)) {
402 // split the base of the framebuffer into a long-aligned address and the
403 // index of the first bit
404 base
= (unsigned long __iomem
*)((unsigned long)p
->screen_base
& ~(bytes
-1));
405 dst_idx
= src_idx
= 8*((unsigned long)p
->screen_base
& (bytes
-1));
406 // add offset of source and target area
407 dst_idx
+= dy
*bits_per_line
+ dx
*p
->var
.bits_per_pixel
;
408 src_idx
+= sy
*bits_per_line
+ sx
*p
->var
.bits_per_pixel
;
410 if (p
->fbops
->fb_sync
)
411 p
->fbops
->fb_sync(p
);
415 dst_idx
-= bits_per_line
;
416 src_idx
-= bits_per_line
;
417 bitcpy_rev(p
, base
+ (dst_idx
/ bits
), dst_idx
% bits
,
418 base
+ (src_idx
/ bits
), src_idx
% bits
, bits
,
419 width
*p
->var
.bits_per_pixel
, bswapmask
);
423 bitcpy(p
, base
+ (dst_idx
/ bits
), dst_idx
% bits
,
424 base
+ (src_idx
/ bits
), src_idx
% bits
, bits
,
425 width
*p
->var
.bits_per_pixel
, bswapmask
);
426 dst_idx
+= bits_per_line
;
427 src_idx
+= bits_per_line
;
432 EXPORT_SYMBOL(cfb_copyarea
);
434 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
435 MODULE_DESCRIPTION("Generic software accelerated copyarea");
436 MODULE_LICENSE("GPL");