Merge branch 'drm-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied...
[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(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
48                 const unsigned long __iomem *src, int src_idx, int bits,
49                 unsigned n, u32 bswapmask)
50 {
51         unsigned long first, last;
52         int const shift = dst_idx-src_idx;
53         int left, right;
54
55         first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
56         last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
57
58         if (!shift) {
59                 // Same alignment for source and dest
60
61                 if (dst_idx+n <= bits) {
62                         // Single word
63                         if (last)
64                                 first &= last;
65                         FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
66                 } else {
67                         // Multiple destination words
68
69                         // Leading bits
70                         if (first != ~0UL) {
71                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
72                                 dst++;
73                                 src++;
74                                 n -= bits - dst_idx;
75                         }
76
77                         // Main chunk
78                         n /= bits;
79                         while (n >= 8) {
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                                 FB_WRITEL(FB_READL(src++), dst++);
88                                 n -= 8;
89                         }
90                         while (n--)
91                                 FB_WRITEL(FB_READL(src++), dst++);
92
93                         // Trailing bits
94                         if (last)
95                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
96                 }
97         } else {
98                 /* Different alignment for source and dest */
99                 unsigned long d0, d1;
100                 int m;
101
102                 right = shift & (bits - 1);
103                 left = -shift & (bits - 1);
104                 bswapmask &= shift;
105
106                 if (dst_idx+n <= bits) {
107                         // Single destination word
108                         if (last)
109                                 first &= last;
110                         d0 = FB_READL(src);
111                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
112                         if (shift > 0) {
113                                 // Single source word
114                                 d0 >>= right;
115                         } else if (src_idx+n <= bits) {
116                                 // Single source word
117                                 d0 <<= left;;
118                         } else {
119                                 // 2 source words
120                                 d1 = FB_READL(src + 1);
121                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
122                                 d0 = d0<<left | d1>>right;
123                         }
124                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
125                         FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
126                 } else {
127                         // Multiple destination words
128                         /** We must always remember the last value read, because in case
129                         SRC and DST overlap bitwise (e.g. when moving just one pixel in
130                         1bpp), we always collect one full long for DST and that might
131                         overlap with the current long from SRC. We store this value in
132                         'd0'. */
133                         d0 = FB_READL(src++);
134                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
135                         // Leading bits
136                         if (shift > 0) {
137                                 // Single source word
138                                 d1 = d0;
139                                 d0 >>= right;
140                                 dst++;
141                                 n -= bits - dst_idx;
142                         } else {
143                                 // 2 source words
144                                 d1 = FB_READL(src++);
145                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
146
147                                 d0 = d0<<left | d1>>right;
148                                 dst++;
149                                 n -= bits - dst_idx;
150                         }
151                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
152                         FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
153                         d0 = d1;
154
155                         // Main chunk
156                         m = n % bits;
157                         n /= bits;
158                         while ((n >= 4) && !bswapmask) {
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                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
176                                 d0 = d0 << left | d1 >> right;
177                                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
178                                 FB_WRITEL(d0, dst++);
179                                 d0 = d1;
180                         }
181
182                         // Trailing bits
183                         if (last) {
184                                 if (m <= right) {
185                                         // Single source word
186                                         d0 <<= left;
187                                 } else {
188                                         // 2 source words
189                                         d1 = FB_READL(src);
190                                         d1 = fb_rev_pixels_in_long(d1,
191                                                                 bswapmask);
192                                         d0 = d0<<left | d1>>right;
193                                 }
194                                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
195                                 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
196                         }
197                 }
198         }
199 }
200
201     /*
202      *  Generic bitwise copy algorithm, operating backward
203      */
204
205 static void
206 bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
207                 const unsigned long __iomem *src, int src_idx, int bits,
208                 unsigned n, u32 bswapmask)
209 {
210         unsigned long first, last;
211         int shift;
212
213         dst += (n-1)/bits;
214         src += (n-1)/bits;
215         if ((n-1) % bits) {
216                 dst_idx += (n-1) % bits;
217                 dst += dst_idx >> (ffs(bits) - 1);
218                 dst_idx &= bits - 1;
219                 src_idx += (n-1) % bits;
220                 src += src_idx >> (ffs(bits) - 1);
221                 src_idx &= bits - 1;
222         }
223
224         shift = dst_idx-src_idx;
225
226         first = fb_shifted_pixels_mask_long(p, bits - 1 - dst_idx, bswapmask);
227         last = ~fb_shifted_pixels_mask_long(p, bits - 1 - ((dst_idx-n) % bits),
228                                             bswapmask);
229
230         if (!shift) {
231                 // Same alignment for source and dest
232
233                 if ((unsigned long)dst_idx+1 >= n) {
234                         // Single word
235                         if (last)
236                                 first &= last;
237                         FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
238                 } else {
239                         // Multiple destination words
240
241                         // Leading bits
242                         if (first != ~0UL) {
243                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
244                                 dst--;
245                                 src--;
246                                 n -= dst_idx+1;
247                         }
248
249                         // Main chunk
250                         n /= bits;
251                         while (n >= 8) {
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                                 FB_WRITEL(FB_READL(src--), dst--);
260                                 n -= 8;
261                         }
262                         while (n--)
263                                 FB_WRITEL(FB_READL(src--), dst--);
264
265                         // Trailing bits
266                         if (last)
267                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
268                 }
269         } else {
270                 // Different alignment for source and dest
271                 unsigned long d0, d1;
272                 int m;
273
274                 int const left = -shift & (bits-1);
275                 int const right = shift & (bits-1);
276                 bswapmask &= shift;
277
278                 if ((unsigned long)dst_idx+1 >= n) {
279                         // Single destination word
280                         if (last)
281                                 first &= last;
282                         d0 = FB_READL(src);
283                         if (shift < 0) {
284                                 // Single source word
285                                 d0 <<= left;
286                         } else if (1+(unsigned long)src_idx >= n) {
287                                 // Single source word
288                                 d0 >>= right;
289                         } else {
290                                 // 2 source words
291                                 d1 = FB_READL(src - 1);
292                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
293                                 d0 = d0>>right | d1<<left;
294                         }
295                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
296                         FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
297                 } else {
298                         // Multiple destination words
299                         /** We must always remember the last value read, because in case
300                         SRC and DST overlap bitwise (e.g. when moving just one pixel in
301                         1bpp), we always collect one full long for DST and that might
302                         overlap with the current long from SRC. We store this value in
303                         'd0'. */
304
305                         d0 = FB_READL(src--);
306                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
307                         // Leading bits
308                         if (shift < 0) {
309                                 // Single source word
310                                 d1 = d0;
311                                 d0 <<= left;
312                         } else {
313                                 // 2 source words
314                                 d1 = FB_READL(src--);
315                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
316                                 d0 = d0>>right | d1<<left;
317                         }
318                         d0 = fb_rev_pixels_in_long(d0, bswapmask);
319                         FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
320                         d0 = d1;
321                         dst--;
322                         n -= dst_idx+1;
323
324                         // Main chunk
325                         m = n % bits;
326                         n /= bits;
327                         while ((n >= 4) && !bswapmask) {
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                                 d1 = FB_READL(src--);
338                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
339                                 d0 = d1;
340                                 n -= 4;
341                         }
342                         while (n--) {
343                                 d1 = FB_READL(src--);
344                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
345                                 d0 = d0 >> right | d1 << left;
346                                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
347                                 FB_WRITEL(d0, dst--);
348                                 d0 = d1;
349                         }
350
351                         // Trailing bits
352                         if (last) {
353                                 if (m <= left) {
354                                         // Single source word
355                                         d0 >>= right;
356                                 } else {
357                                         // 2 source words
358                                         d1 = FB_READL(src);
359                                         d1 = fb_rev_pixels_in_long(d1,
360                                                                 bswapmask);
361                                         d0 = d0>>right | d1<<left;
362                                 }
363                                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
364                                 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
365                         }
366                 }
367         }
368 }
369
370 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
371 {
372         u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
373         u32 height = area->height, width = area->width;
374         unsigned long const bits_per_line = p->fix.line_length*8u;
375         unsigned long __iomem *dst = NULL, *src = NULL;
376         int bits = BITS_PER_LONG, bytes = bits >> 3;
377         int dst_idx = 0, src_idx = 0, rev_copy = 0;
378         u32 bswapmask = fb_compute_bswapmask(p);
379
380         if (p->state != FBINFO_STATE_RUNNING)
381                 return;
382
383         /* if the beginning of the target area might overlap with the end of
384         the source area, be have to copy the area reverse. */
385         if ((dy == sy && dx > sx) || (dy > sy)) {
386                 dy += height;
387                 sy += height;
388                 rev_copy = 1;
389         }
390
391         // split the base of the framebuffer into a long-aligned address and the
392         // index of the first bit
393         dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
394         dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
395         // add offset of source and target area
396         dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
397         src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
398
399         if (p->fbops->fb_sync)
400                 p->fbops->fb_sync(p);
401
402         if (rev_copy) {
403                 while (height--) {
404                         dst_idx -= bits_per_line;
405                         src_idx -= bits_per_line;
406                         dst += dst_idx >> (ffs(bits) - 1);
407                         dst_idx &= (bytes - 1);
408                         src += src_idx >> (ffs(bits) - 1);
409                         src_idx &= (bytes - 1);
410                         bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
411                                 width*p->var.bits_per_pixel, bswapmask);
412                 }
413         } else {
414                 while (height--) {
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(p, dst, dst_idx, src, src_idx, bits,
420                                 width*p->var.bits_per_pixel, bswapmask);
421                         dst_idx += bits_per_line;
422                         src_idx += bits_per_line;
423                 }
424         }
425 }
426
427 EXPORT_SYMBOL(cfb_copyarea);
428
429 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
430 MODULE_DESCRIPTION("Generic software accelerated copyarea");
431 MODULE_LICENSE("GPL");
432