Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / video / cfbcopyarea.c
1 /*
2  *  Generic function for frame buffer with packed pixels of any depth.
3  *
4  *      Copyright (C)  1999-2005 James Simmons <jsimmons@www.infradead.org>
5  *
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
8  *  more details.
9  *
10  * NOTES:
11  *
12  *  This is for cfb packed pixels. Iplan and such are incorporated in the
13  *  drivers that need them.
14  *
15  *  FIXME
16  *
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.
19  *
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.
23  */
24
25
26
27 #include <linux/module.h>
28 #include <linux/kernel.h>
29 #include <linux/string.h>
30 #include <linux/fb.h>
31 #include <linux/slab.h>
32 #include <asm/types.h>
33 #include <asm/io.h>
34
35 #if BITS_PER_LONG == 32
36 #  define FB_WRITEL fb_writel
37 #  define FB_READL  fb_readl
38 #else
39 #  define FB_WRITEL fb_writeq
40 #  define FB_READL  fb_readq
41 #endif
42
43     /*
44      *  Compose two values, using a bitmask as decision value
45      *  This is equivalent to (a & mask) | (b & ~mask)
46      */
47
48 static inline unsigned long
49 comp(unsigned long a, unsigned long b, unsigned long mask)
50 {
51     return ((a ^ b) & mask) ^ b;
52 }
53
54     /*
55      *  Generic bitwise copy algorithm
56      */
57
58 static void
59 bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
60         int src_idx, int bits, unsigned n)
61 {
62         unsigned long first, last;
63         int const shift = dst_idx-src_idx;
64         int left, right;
65
66         first = FB_SHIFT_HIGH(~0UL, dst_idx);
67         last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
68
69         if (!shift) {
70                 // Same alignment for source and dest
71
72                 if (dst_idx+n <= bits) {
73                         // Single word
74                         if (last)
75                                 first &= last;
76                         FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
77                 } else {
78                         // Multiple destination words
79
80                         // Leading bits
81                         if (first != ~0UL) {
82                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
83                                 dst++;
84                                 src++;
85                                 n -= bits - dst_idx;
86                         }
87
88                         // Main chunk
89                         n /= bits;
90                         while (n >= 8) {
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++);
99                                 n -= 8;
100                         }
101                         while (n--)
102                                 FB_WRITEL(FB_READL(src++), dst++);
103
104                         // Trailing bits
105                         if (last)
106                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
107                 }
108         } else {
109                 unsigned long d0, d1;
110                 int m;
111                 // Different alignment for source and dest
112
113                 right = shift & (bits - 1);
114                 left = -shift & (bits - 1);
115
116                 if (dst_idx+n <= bits) {
117                         // Single destination word
118                         if (last)
119                                 first &= last;
120                         if (shift > 0) {
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);
126                         } else {
127                                 // 2 source words
128                                 d0 = FB_READL(src++);
129                                 d1 = FB_READL(src);
130                                 FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
131                         }
132                 } else {
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
138                         'd0'. */
139                         d0 = FB_READL(src++);
140                         // Leading bits
141                         if (shift > 0) {
142                                 // Single source word
143                                 FB_WRITEL( comp(d0 >> right, FB_READL(dst), first), dst);
144                                 dst++;
145                                 n -= bits - dst_idx;
146                         } else {
147                                 // 2 source words
148                                 d1 = FB_READL(src++);
149                                 FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
150                                 d0 = d1;
151                                 dst++;
152                                 n -= bits - dst_idx;
153                         }
154
155                         // Main chunk
156                         m = n % bits;
157                         n /= bits;
158                         while (n >= 4) {
159                                 d1 = FB_READL(src++);
160                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
161                                 d0 = d1;
162                                 d1 = FB_READL(src++);
163                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
164                                 d0 = d1;
165                                 d1 = FB_READL(src++);
166                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
167                                 d0 = d1;
168                                 d1 = FB_READL(src++);
169                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
170                                 d0 = d1;
171                                 n -= 4;
172                         }
173                         while (n--) {
174                                 d1 = FB_READL(src++);
175                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
176                                 d0 = d1;
177                         }
178
179                         // Trailing bits
180                         if (last) {
181                                 if (m <= right) {
182                                         // Single source word
183                                         FB_WRITEL( comp(d0 << left, FB_READL(dst), last), dst);
184                                 } else {
185                                         // 2 source words
186                                         d1 = FB_READL(src);
187                                         FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), last), dst);
188                                 }
189                         }
190                 }
191         }
192 }
193
194     /*
195      *  Generic bitwise copy algorithm, operating backward
196      */
197
198 static void
199 bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
200                 int src_idx, int bits, unsigned n)
201 {
202         unsigned long first, last;
203         int shift;
204
205         dst += (n-1)/bits;
206         src += (n-1)/bits;
207         if ((n-1) % bits) {
208                 dst_idx += (n-1) % bits;
209                 dst += dst_idx >> (ffs(bits) - 1);
210                 dst_idx &= bits - 1;
211                 src_idx += (n-1) % bits;
212                 src += src_idx >> (ffs(bits) - 1);
213                 src_idx &= bits - 1;
214         }
215
216         shift = dst_idx-src_idx;
217
218         first = FB_SHIFT_LOW(~0UL, bits - 1 - dst_idx);
219         last = ~(FB_SHIFT_LOW(~0UL, bits - 1 - ((dst_idx-n) % bits)));
220
221         if (!shift) {
222                 // Same alignment for source and dest
223
224                 if ((unsigned long)dst_idx+1 >= n) {
225                         // Single word
226                         if (last)
227                                 first &= last;
228                         FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
229                 } else {
230                         // Multiple destination words
231
232                         // Leading bits
233                         if (first != ~0UL) {
234                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
235                                 dst--;
236                                 src--;
237                                 n -= dst_idx+1;
238                         }
239
240                         // Main chunk
241                         n /= bits;
242                         while (n >= 8) {
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--);
251                                 n -= 8;
252                         }
253                         while (n--)
254                                 FB_WRITEL(FB_READL(src--), dst--);
255
256                         // Trailing bits
257                         if (last)
258                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
259                 }
260         } else {
261                 // Different alignment for source and dest
262
263                 int const left = -shift & (bits-1);
264                 int const right = shift & (bits-1);
265
266                 if ((unsigned long)dst_idx+1 >= n) {
267                         // Single destination word
268                         if (last)
269                                 first &= last;
270                         if (shift < 0) {
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);
276                         } else {
277                                 // 2 source words
278                                 FB_WRITEL( comp( (FB_READL(src)>>right | FB_READL(src-1)<<left), FB_READL(dst), first), dst);
279                         }
280                 } else {
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
286                         'd0'. */
287                         unsigned long d0, d1;
288                         int m;
289
290                         d0 = FB_READL(src--);
291                         // Leading bits
292                         if (shift < 0) {
293                                 // Single source word
294                                 FB_WRITEL( comp( (d0 << left), FB_READL(dst), first), dst);
295                         } else {
296                                 // 2 source words
297                                 d1 = FB_READL(src--);
298                                 FB_WRITEL( comp( (d0>>right | d1<<left), FB_READL(dst), first), dst);
299                                 d0 = d1;
300                         }
301                         dst--;
302                         n -= dst_idx+1;
303
304                         // Main chunk
305                         m = n % bits;
306                         n /= bits;
307                         while (n >= 4) {
308                                 d1 = FB_READL(src--);
309                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
310                                 d0 = d1;
311                                 d1 = FB_READL(src--);
312                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
313                                 d0 = d1;
314                                 d1 = FB_READL(src--);
315                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
316                                 d0 = d1;
317                                 d1 = FB_READL(src--);
318                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
319                                 d0 = d1;
320                                 n -= 4;
321                         }
322                         while (n--) {
323                                 d1 = FB_READL(src--);
324                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
325                                 d0 = d1;
326                         }
327
328                         // Trailing bits
329                         if (last) {
330                                 if (m <= left) {
331                                         // Single source word
332                                         FB_WRITEL( comp(d0 >> right, FB_READL(dst), last), dst);
333                                 } else {
334                                         // 2 source words
335                                         d1 = FB_READL(src);
336                                         FB_WRITEL( comp(d0>>right | d1<<left, FB_READL(dst), last), dst);
337                                 }
338                         }
339                 }
340         }
341 }
342
343 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
344 {
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;
351
352         if (p->state != FBINFO_STATE_RUNNING)
353                 return;
354
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)) {
358                 dy += height;
359                 sy += height;
360                 rev_copy = 1;
361         }
362
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;
370
371         if (p->fbops->fb_sync)
372                 p->fbops->fb_sync(p);
373
374         if (rev_copy) {
375                 while (height--) {
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);
384                 }
385         } else {
386                 while (height--) {
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;
395                 }
396         }
397 }
398
399 EXPORT_SYMBOL(cfb_copyarea);
400
401 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
402 MODULE_DESCRIPTION("Generic software accelerated copyarea");
403 MODULE_LICENSE("GPL");
404