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/config.h>
28 #include <linux/module.h>
29 #include <linux/kernel.h>
30 #include <linux/string.h>
32 #include <linux/slab.h>
33 #include <asm/types.h>
36 #if BITS_PER_LONG == 32
37 # define FB_WRITEL fb_writel
38 # define FB_READL fb_readl
40 # define FB_WRITEL fb_writeq
41 # define FB_READL fb_readq
45 * Compose two values, using a bitmask as decision value
46 * This is equivalent to (a & mask) | (b & ~mask)
49 static inline unsigned long
50 comp(unsigned long a
, unsigned long b
, unsigned long mask
)
52 return ((a
^ b
) & mask
) ^ b
;
56 * Generic bitwise copy algorithm
60 bitcpy(unsigned long __iomem
*dst
, int dst_idx
, const unsigned long __iomem
*src
,
61 int src_idx
, int bits
, unsigned n
)
63 unsigned long first
, last
;
64 int const shift
= dst_idx
-src_idx
;
67 first
= ~0UL >> dst_idx
;
68 last
= ~(~0UL >> ((dst_idx
+n
) % bits
));
71 // Same alignment for source and dest
73 if (dst_idx
+n
<= bits
) {
77 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
79 // Multiple destination words
83 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), 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
++);
99 FB_WRITEL(FB_READL(src
++), dst
++);
103 FB_WRITEL(FB_READL(src
++), dst
++);
107 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), last
), dst
);
110 unsigned long d0
, d1
;
112 // Different alignment for source and dest
114 right
= shift
& (bits
- 1);
115 left
= -shift
& (bits
- 1);
117 if (dst_idx
+n
<= bits
) {
118 // Single destination word
122 // Single source word
123 FB_WRITEL( comp( FB_READL(src
) >> right
, FB_READL(dst
), first
), dst
);
124 } else if (src_idx
+n
<= bits
) {
125 // Single source word
126 FB_WRITEL( comp(FB_READL(src
) << left
, FB_READL(dst
), first
), dst
);
129 d0
= FB_READL(src
++);
131 FB_WRITEL( comp(d0
<<left
| d1
>>right
, 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
++);
143 // Single source word
144 FB_WRITEL( comp(d0
>> right
, FB_READL(dst
), first
), dst
);
149 d1
= FB_READL(src
++);
150 FB_WRITEL( comp(d0
<<left
| d1
>>right
, FB_READL(dst
), first
), dst
);
160 d1
= FB_READL(src
++);
161 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
163 d1
= FB_READL(src
++);
164 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
166 d1
= FB_READL(src
++);
167 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
169 d1
= FB_READL(src
++);
170 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
175 d1
= FB_READL(src
++);
176 FB_WRITEL(d0
<< left
| d1
>> right
, dst
++);
183 // Single source word
184 FB_WRITEL( comp(d0
<< left
, FB_READL(dst
), last
), dst
);
188 FB_WRITEL( comp(d0
<<left
| d1
>>right
, FB_READL(dst
), last
), dst
);
196 * Generic bitwise copy algorithm, operating backward
200 bitcpy_rev(unsigned long __iomem
*dst
, int dst_idx
, const unsigned long __iomem
*src
,
201 int src_idx
, int bits
, unsigned n
)
203 unsigned long first
, last
;
209 dst_idx
+= (n
-1) % bits
;
210 dst
+= dst_idx
>> (ffs(bits
) - 1);
212 src_idx
+= (n
-1) % bits
;
213 src
+= src_idx
>> (ffs(bits
) - 1);
217 shift
= dst_idx
-src_idx
;
219 first
= ~0UL << (bits
- 1 - dst_idx
);
220 last
= ~(~0UL << (bits
- 1 - ((dst_idx
-n
) % bits
)));
223 // Same alignment for source and dest
225 if ((unsigned long)dst_idx
+1 >= n
) {
229 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), dst
);
231 // Multiple destination words
235 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), first
), 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
--);
251 FB_WRITEL(FB_READL(src
--), dst
--);
255 FB_WRITEL(FB_READL(src
--), dst
--);
259 FB_WRITEL( comp( FB_READL(src
), FB_READL(dst
), last
), dst
);
262 // Different alignment for source and dest
264 int const left
= -shift
& (bits
-1);
265 int const right
= shift
& (bits
-1);
267 if ((unsigned long)dst_idx
+1 >= n
) {
268 // Single destination word
272 // Single source word
273 FB_WRITEL( comp( FB_READL(src
)<<left
, FB_READL(dst
), first
), dst
);
274 } else if (1+(unsigned long)src_idx
>= n
) {
275 // Single source word
276 FB_WRITEL( comp( FB_READL(src
)>>right
, FB_READL(dst
), first
), dst
);
279 FB_WRITEL( comp( (FB_READL(src
)>>right
| FB_READL(src
-1)<<left
), FB_READL(dst
), first
), dst
);
282 // Multiple destination words
283 /** We must always remember the last value read, because in case
284 SRC and DST overlap bitwise (e.g. when moving just one pixel in
285 1bpp), we always collect one full long for DST and that might
286 overlap with the current long from SRC. We store this value in
288 unsigned long d0
, d1
;
291 d0
= FB_READL(src
--);
294 // Single source word
295 FB_WRITEL( comp( (d0
<< left
), FB_READL(dst
), first
), dst
);
298 d1
= FB_READL(src
--);
299 FB_WRITEL( comp( (d0
>>right
| d1
<<left
), FB_READL(dst
), first
), dst
);
309 d1
= FB_READL(src
--);
310 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
312 d1
= FB_READL(src
--);
313 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
315 d1
= FB_READL(src
--);
316 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
318 d1
= FB_READL(src
--);
319 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
324 d1
= FB_READL(src
--);
325 FB_WRITEL(d0
>> right
| d1
<< left
, dst
--);
332 // Single source word
333 FB_WRITEL( comp(d0
>> right
, FB_READL(dst
), last
), dst
);
337 FB_WRITEL( comp(d0
>>right
| d1
<<left
, FB_READL(dst
), last
), dst
);
344 void cfb_copyarea(struct fb_info
*p
, const struct fb_copyarea
*area
)
346 u32 dx
= area
->dx
, dy
= area
->dy
, sx
= area
->sx
, sy
= area
->sy
;
347 u32 height
= area
->height
, width
= area
->width
;
348 unsigned long const bits_per_line
= p
->fix
.line_length
*8u;
349 unsigned long __iomem
*dst
= NULL
, *src
= NULL
;
350 int bits
= BITS_PER_LONG
, bytes
= bits
>> 3;
351 int dst_idx
= 0, src_idx
= 0, rev_copy
= 0;
352 int x2
, y2
, vxres
, vyres
;
354 if (p
->state
!= FBINFO_STATE_RUNNING
)
357 /* We want rotation but lack hardware to do it for us. */
358 if (!p
->fbops
->fb_rotate
&& p
->var
.rotate
) {
361 vxres
= p
->var
.xres_virtual
;
362 vyres
= p
->var
.yres_virtual
;
364 if (area
->dx
> vxres
|| area
->sx
> vxres
||
365 area
->dy
> vyres
|| area
->sy
> vyres
)
368 /* clip the destination
369 * We could use hardware clipping but on many cards you get around
370 * hardware clipping by writing to framebuffer directly.
372 x2
= area
->dx
+ area
->width
;
373 y2
= area
->dy
+ area
->height
;
374 dx
= area
->dx
> 0 ? area
->dx
: 0;
375 dy
= area
->dy
> 0 ? area
->dy
: 0;
376 x2
= x2
< vxres
? x2
: vxres
;
377 y2
= y2
< vyres
? y2
: vyres
;
381 if ((width
==0) ||(height
==0))
385 sx
+= (dx
- area
->dx
);
386 sy
+= (dy
- area
->dy
);
388 /* the source must be completely inside the virtual screen */
389 if (sx
< 0 || sy
< 0 || (sx
+ width
) > vxres
|| (sy
+ height
) > vyres
)
392 /* if the beginning of the target area might overlap with the end of
393 the source area, be have to copy the area reverse. */
394 if ((dy
== sy
&& dx
> sx
) || (dy
> sy
)) {
400 // split the base of the framebuffer into a long-aligned address and the
401 // index of the first bit
402 dst
= src
= (unsigned long __iomem
*)((unsigned long)p
->screen_base
& ~(bytes
-1));
403 dst_idx
= src_idx
= 8*((unsigned long)p
->screen_base
& (bytes
-1));
404 // add offset of source and target area
405 dst_idx
+= dy
*bits_per_line
+ dx
*p
->var
.bits_per_pixel
;
406 src_idx
+= sy
*bits_per_line
+ sx
*p
->var
.bits_per_pixel
;
408 if (p
->fbops
->fb_sync
)
409 p
->fbops
->fb_sync(p
);
413 dst_idx
-= bits_per_line
;
414 src_idx
-= bits_per_line
;
415 dst
+= dst_idx
>> (ffs(bits
) - 1);
416 dst_idx
&= (bytes
- 1);
417 src
+= src_idx
>> (ffs(bits
) - 1);
418 src_idx
&= (bytes
- 1);
419 bitcpy_rev(dst
, dst_idx
, src
, src_idx
, bits
,
420 width
*p
->var
.bits_per_pixel
);
424 dst
+= dst_idx
>> (ffs(bits
) - 1);
425 dst_idx
&= (bytes
- 1);
426 src
+= src_idx
>> (ffs(bits
) - 1);
427 src_idx
&= (bytes
- 1);
428 bitcpy(dst
, dst_idx
, src
, src_idx
, bits
,
429 width
*p
->var
.bits_per_pixel
);
430 dst_idx
+= bits_per_line
;
431 src_idx
+= bits_per_line
;
436 EXPORT_SYMBOL(cfb_copyarea
);
438 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
439 MODULE_DESCRIPTION("Generic software accelerated copyarea");
440 MODULE_LICENSE("GPL");