b07e419b12d289f79dfeb2a34f1e87c66b12c971
[safe/jmp/linux-2.6] / 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 #include <linux/module.h>
26 #include <linux/kernel.h>
27 #include <linux/string.h>
28 #include <linux/fb.h>
29 #include <linux/slab.h>
30 #include <asm/types.h>
31 #include <asm/io.h>
32 #include "fb_draw.h"
33
34 #if BITS_PER_LONG == 32
35 #  define FB_WRITEL fb_writel
36 #  define FB_READL  fb_readl
37 #else
38 #  define FB_WRITEL fb_writeq
39 #  define FB_READL  fb_readq
40 #endif
41
42     /*
43      *  Generic bitwise copy algorithm
44      */
45
46 static void
47 bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
48         int src_idx, int bits, unsigned n, u32 bswapmask)
49 {
50         unsigned long first, last;
51         int const shift = dst_idx-src_idx;
52         int left, right;
53
54         first = fb_shifted_pixels_mask_long(dst_idx, bswapmask);
55         last = ~fb_shifted_pixels_mask_long((dst_idx+n) % bits, bswapmask);
56
57         if (!shift) {
58                 // Same alignment for source and dest
59
60                 if (dst_idx+n <= bits) {
61                         // Single word
62                         if (last)
63                                 first &= last;
64                         FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
65                 } else {
66                         // Multiple destination words
67
68                         // Leading bits
69                         if (first != ~0UL) {
70                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
71                                 dst++;
72                                 src++;
73                                 n -= bits - dst_idx;
74                         }
75
76                         // Main chunk
77                         n /= bits;
78                         while (n >= 8) {
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++);
87                                 n -= 8;
88                         }
89                         while (n--)
90                                 FB_WRITEL(FB_READL(src++), dst++);
91
92                         // Trailing bits
93                         if (last)
94                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
95                 }
96         } else {
97                 /* Different alignment for source and dest */
98                 unsigned long d0, d1;
99                 int m;
100
101                 right = shift & (bits - 1);
102                 left = -shift & (bits - 1);
103                 bswapmask &= shift;
104
105                 if (dst_idx+n <= bits) {
106                         // Single destination word
107                         if (last)
108                                 first &= last;
109                         d0 = FB_READL(src);
110                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
111                         if (shift > 0) {
112                                 // Single source word
113                                 d0 >>= right;
114                         } else if (src_idx+n <= bits) {
115                                 // Single source word
116                                 d0 <<= left;;
117                         } else {
118                                 // 2 source words
119                                 d1 = FB_READL(src + 1);
120                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
121                                 d0 = d0<<left | d1>>right;
122                         }
123                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
124                         FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
125                 } else {
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
131                         'd0'. */
132                         d0 = FB_READL(src++);
133                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
134                         // Leading bits
135                         if (shift > 0) {
136                                 // Single source word
137                                 d1 = d0;
138                                 d0 >>= right;
139                                 dst++;
140                                 n -= bits - dst_idx;
141                         } else {
142                                 // 2 source words
143                                 d1 = FB_READL(src++);
144                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
145
146                                 d0 = d0<<left | d1>>right;
147                                 dst++;
148                                 n -= bits - dst_idx;
149                         }
150                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
151                         FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
152                         d0 = d1;
153
154                         // Main chunk
155                         m = n % bits;
156                         n /= bits;
157                         while ((n >= 4) && !bswapmask) {
158                                 d1 = FB_READL(src++);
159                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
160                                 d0 = d1;
161                                 d1 = FB_READL(src++);
162                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
163                                 d0 = d1;
164                                 d1 = FB_READL(src++);
165                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
166                                 d0 = d1;
167                                 d1 = FB_READL(src++);
168                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
169                                 d0 = d1;
170                                 n -= 4;
171                         }
172                         while (n--) {
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++);
178                                 d0 = d1;
179                         }
180
181                         // Trailing bits
182                         if (last) {
183                                 if (m <= right) {
184                                         // Single source word
185                                         d0 <<= left;
186                                 } else {
187                                         // 2 source words
188                                         d1 = FB_READL(src);
189                                         d1 = fb_rev_pixels_in_long(d1,
190                                                                 bswapmask);
191                                         d0 = d0<<left | d1>>right;
192                                 }
193                                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
194                                 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
195                         }
196                 }
197         }
198 }
199
200     /*
201      *  Generic bitwise copy algorithm, operating backward
202      */
203
204 static void
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)
207 {
208         unsigned long first, last;
209         int shift;
210
211         dst += (n-1)/bits;
212         src += (n-1)/bits;
213         if ((n-1) % bits) {
214                 dst_idx += (n-1) % bits;
215                 dst += dst_idx >> (ffs(bits) - 1);
216                 dst_idx &= bits - 1;
217                 src_idx += (n-1) % bits;
218                 src += src_idx >> (ffs(bits) - 1);
219                 src_idx &= bits - 1;
220         }
221
222         shift = dst_idx-src_idx;
223
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);
226
227         if (!shift) {
228                 // Same alignment for source and dest
229
230                 if ((unsigned long)dst_idx+1 >= n) {
231                         // Single word
232                         if (last)
233                                 first &= last;
234                         FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
235                 } else {
236                         // Multiple destination words
237
238                         // Leading bits
239                         if (first != ~0UL) {
240                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
241                                 dst--;
242                                 src--;
243                                 n -= dst_idx+1;
244                         }
245
246                         // Main chunk
247                         n /= bits;
248                         while (n >= 8) {
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--);
257                                 n -= 8;
258                         }
259                         while (n--)
260                                 FB_WRITEL(FB_READL(src--), dst--);
261
262                         // Trailing bits
263                         if (last)
264                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
265                 }
266         } else {
267                 // Different alignment for source and dest
268                 unsigned long d0, d1;
269                 int m;
270
271                 int const left = -shift & (bits-1);
272                 int const right = shift & (bits-1);
273                 bswapmask &= shift;
274
275                 if ((unsigned long)dst_idx+1 >= n) {
276                         // Single destination word
277                         if (last)
278                                 first &= last;
279                         d0 = FB_READL(src);
280                         if (shift < 0) {
281                                 // Single source word
282                                 d0 <<= left;
283                         } else if (1+(unsigned long)src_idx >= n) {
284                                 // Single source word
285                                 d0 >>= right;
286                         } else {
287                                 // 2 source words
288                                 d1 = FB_READL(src - 1);
289                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
290                                 d0 = d0>>right | d1<<left;
291                         }
292                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
293                         FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
294                 } else {
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
300                         'd0'. */
301
302                         d0 = FB_READL(src--);
303                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
304                         // Leading bits
305                         if (shift < 0) {
306                                 // Single source word
307                                 d1 = d0;
308                                 d0 <<= left;
309                         } else {
310                                 // 2 source words
311                                 d1 = FB_READL(src--);
312                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
313                                 d0 = d0>>right | d1<<left;
314                         }
315                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
316                         FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
317                         d0 = d1;
318                         dst--;
319                         n -= dst_idx+1;
320
321                         // Main chunk
322                         m = n % bits;
323                         n /= bits;
324                         while ((n >= 4) && !bswapmask) {
325                                 d1 = FB_READL(src--);
326                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
327                                 d0 = d1;
328                                 d1 = FB_READL(src--);
329                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
330                                 d0 = d1;
331                                 d1 = FB_READL(src--);
332                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
333                                 d0 = d1;
334                                 d1 = FB_READL(src--);
335                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
336                                 d0 = d1;
337                                 n -= 4;
338                         }
339                         while (n--) {
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--);
345                                 d0 = d1;
346                         }
347
348                         // Trailing bits
349                         if (last) {
350                                 if (m <= left) {
351                                         // Single source word
352                                         d0 >>= right;
353                                 } else {
354                                         // 2 source words
355                                         d1 = FB_READL(src);
356                                         d1 = fb_rev_pixels_in_long(d1,
357                                                                 bswapmask);
358                                         d0 = d0>>right | d1<<left;
359                                 }
360                                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
361                                 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
362                         }
363                 }
364         }
365 }
366
367 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
368 {
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);
376
377         if (p->state != FBINFO_STATE_RUNNING)
378                 return;
379
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)) {
383                 dy += height;
384                 sy += height;
385                 rev_copy = 1;
386         }
387
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;
395
396         if (p->fbops->fb_sync)
397                 p->fbops->fb_sync(p);
398
399         if (rev_copy) {
400                 while (height--) {
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);
409                 }
410         } else {
411                 while (height--) {
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;
420                 }
421         }
422 }
423
424 EXPORT_SYMBOL(cfb_copyarea);
425
426 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
427 MODULE_DESCRIPTION("Generic software accelerated copyarea");
428 MODULE_LICENSE("GPL");
429