fbdev: move FBIO_WAITFORVSYNC to linux/fb.h
[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 <asm/types.h>
30 #include <asm/io.h>
31 #include "fb_draw.h"
32
33 #if BITS_PER_LONG == 32
34 #  define FB_WRITEL fb_writel
35 #  define FB_READL  fb_readl
36 #else
37 #  define FB_WRITEL fb_writeq
38 #  define FB_READL  fb_readq
39 #endif
40
41     /*
42      *  Generic bitwise copy algorithm
43      */
44
45 static void
46 bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
47                 const unsigned long __iomem *src, int src_idx, int bits,
48                 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(p, dst_idx, bswapmask);
55         last = ~fb_shifted_pixels_mask_long(p, (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(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
206                 const unsigned long __iomem *src, int src_idx, int bits,
207                 unsigned n, u32 bswapmask)
208 {
209         unsigned long first, last;
210         int shift;
211
212         dst += (n-1)/bits;
213         src += (n-1)/bits;
214         if ((n-1) % bits) {
215                 dst_idx += (n-1) % bits;
216                 dst += dst_idx >> (ffs(bits) - 1);
217                 dst_idx &= bits - 1;
218                 src_idx += (n-1) % bits;
219                 src += src_idx >> (ffs(bits) - 1);
220                 src_idx &= bits - 1;
221         }
222
223         shift = dst_idx-src_idx;
224
225         first = fb_shifted_pixels_mask_long(p, bits - 1 - dst_idx, bswapmask);
226         last = ~fb_shifted_pixels_mask_long(p, bits - 1 - ((dst_idx-n) % bits),
227                                             bswapmask);
228
229         if (!shift) {
230                 // Same alignment for source and dest
231
232                 if ((unsigned long)dst_idx+1 >= n) {
233                         // Single word
234                         if (last)
235                                 first &= last;
236                         FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
237                 } else {
238                         // Multiple destination words
239
240                         // Leading bits
241                         if (first != ~0UL) {
242                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
243                                 dst--;
244                                 src--;
245                                 n -= dst_idx+1;
246                         }
247
248                         // Main chunk
249                         n /= bits;
250                         while (n >= 8) {
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                                 FB_WRITEL(FB_READL(src--), dst--);
258                                 FB_WRITEL(FB_READL(src--), dst--);
259                                 n -= 8;
260                         }
261                         while (n--)
262                                 FB_WRITEL(FB_READL(src--), dst--);
263
264                         // Trailing bits
265                         if (last)
266                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
267                 }
268         } else {
269                 // Different alignment for source and dest
270                 unsigned long d0, d1;
271                 int m;
272
273                 int const left = -shift & (bits-1);
274                 int const right = shift & (bits-1);
275                 bswapmask &= shift;
276
277                 if ((unsigned long)dst_idx+1 >= n) {
278                         // Single destination word
279                         if (last)
280                                 first &= last;
281                         d0 = FB_READL(src);
282                         if (shift < 0) {
283                                 // Single source word
284                                 d0 <<= left;
285                         } else if (1+(unsigned long)src_idx >= n) {
286                                 // Single source word
287                                 d0 >>= right;
288                         } else {
289                                 // 2 source words
290                                 d1 = FB_READL(src - 1);
291                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
292                                 d0 = d0>>right | d1<<left;
293                         }
294                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
295                         FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
296                 } else {
297                         // Multiple destination words
298                         /** We must always remember the last value read, because in case
299                         SRC and DST overlap bitwise (e.g. when moving just one pixel in
300                         1bpp), we always collect one full long for DST and that might
301                         overlap with the current long from SRC. We store this value in
302                         'd0'. */
303
304                         d0 = FB_READL(src--);
305                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
306                         // Leading bits
307                         if (shift < 0) {
308                                 // Single source word
309                                 d1 = d0;
310                                 d0 <<= left;
311                         } else {
312                                 // 2 source words
313                                 d1 = FB_READL(src--);
314                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
315                                 d0 = d0>>right | d1<<left;
316                         }
317                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
318                         FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
319                         d0 = d1;
320                         dst--;
321                         n -= dst_idx+1;
322
323                         // Main chunk
324                         m = n % bits;
325                         n /= bits;
326                         while ((n >= 4) && !bswapmask) {
327                                 d1 = FB_READL(src--);
328                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
329                                 d0 = d1;
330                                 d1 = FB_READL(src--);
331                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
332                                 d0 = d1;
333                                 d1 = FB_READL(src--);
334                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
335                                 d0 = d1;
336                                 d1 = FB_READL(src--);
337                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
338                                 d0 = d1;
339                                 n -= 4;
340                         }
341                         while (n--) {
342                                 d1 = FB_READL(src--);
343                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
344                                 d0 = d0 >> right | d1 << left;
345                                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
346                                 FB_WRITEL(d0, dst--);
347                                 d0 = d1;
348                         }
349
350                         // Trailing bits
351                         if (last) {
352                                 if (m <= left) {
353                                         // Single source word
354                                         d0 >>= right;
355                                 } else {
356                                         // 2 source words
357                                         d1 = FB_READL(src);
358                                         d1 = fb_rev_pixels_in_long(d1,
359                                                                 bswapmask);
360                                         d0 = d0>>right | d1<<left;
361                                 }
362                                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
363                                 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
364                         }
365                 }
366         }
367 }
368
369 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
370 {
371         u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
372         u32 height = area->height, width = area->width;
373         unsigned long const bits_per_line = p->fix.line_length*8u;
374         unsigned long __iomem *dst = NULL, *src = NULL;
375         int bits = BITS_PER_LONG, bytes = bits >> 3;
376         int dst_idx = 0, src_idx = 0, rev_copy = 0;
377         u32 bswapmask = fb_compute_bswapmask(p);
378
379         if (p->state != FBINFO_STATE_RUNNING)
380                 return;
381
382         /* if the beginning of the target area might overlap with the end of
383         the source area, be have to copy the area reverse. */
384         if ((dy == sy && dx > sx) || (dy > sy)) {
385                 dy += height;
386                 sy += height;
387                 rev_copy = 1;
388         }
389
390         // split the base of the framebuffer into a long-aligned address and the
391         // index of the first bit
392         dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
393         dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
394         // add offset of source and target area
395         dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
396         src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
397
398         if (p->fbops->fb_sync)
399                 p->fbops->fb_sync(p);
400
401         if (rev_copy) {
402                 while (height--) {
403                         dst_idx -= bits_per_line;
404                         src_idx -= bits_per_line;
405                         dst += dst_idx >> (ffs(bits) - 1);
406                         dst_idx &= (bytes - 1);
407                         src += src_idx >> (ffs(bits) - 1);
408                         src_idx &= (bytes - 1);
409                         bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
410                                 width*p->var.bits_per_pixel, bswapmask);
411                 }
412         } else {
413                 while (height--) {
414                         dst += dst_idx >> (ffs(bits) - 1);
415                         dst_idx &= (bytes - 1);
416                         src += src_idx >> (ffs(bits) - 1);
417                         src_idx &= (bytes - 1);
418                         bitcpy(p, dst, dst_idx, src, src_idx, bits,
419                                 width*p->var.bits_per_pixel, bswapmask);
420                         dst_idx += bits_per_line;
421                         src_idx += bits_per_line;
422                 }
423         }
424 }
425
426 EXPORT_SYMBOL(cfb_copyarea);
427
428 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
429 MODULE_DESCRIPTION("Generic software accelerated copyarea");
430 MODULE_LICENSE("GPL");
431