Merge master.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
[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)
49 {
50         unsigned long first, last;
51         int const shift = dst_idx-src_idx;
52         int left, right;
53
54         first = FB_SHIFT_HIGH(~0UL, dst_idx);
55         last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
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                 unsigned long d0, d1;
98                 int m;
99                 // Different alignment for source and dest
100
101                 right = shift & (bits - 1);
102                 left = -shift & (bits - 1);
103
104                 if (dst_idx+n <= bits) {
105                         // Single destination word
106                         if (last)
107                                 first &= last;
108                         if (shift > 0) {
109                                 // Single source word
110                                 FB_WRITEL( comp( FB_READL(src) >> right, FB_READL(dst), first), dst);
111                         } else if (src_idx+n <= bits) {
112                                 // Single source word
113                                 FB_WRITEL( comp(FB_READL(src) << left, FB_READL(dst), first), dst);
114                         } else {
115                                 // 2 source words
116                                 d0 = FB_READL(src++);
117                                 d1 = FB_READL(src);
118                                 FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
119                         }
120                 } else {
121                         // Multiple destination words
122                         /** We must always remember the last value read, because in case
123                         SRC and DST overlap bitwise (e.g. when moving just one pixel in
124                         1bpp), we always collect one full long for DST and that might
125                         overlap with the current long from SRC. We store this value in
126                         'd0'. */
127                         d0 = FB_READL(src++);
128                         // Leading bits
129                         if (shift > 0) {
130                                 // Single source word
131                                 FB_WRITEL( comp(d0 >> right, FB_READL(dst), first), dst);
132                                 dst++;
133                                 n -= bits - dst_idx;
134                         } else {
135                                 // 2 source words
136                                 d1 = FB_READL(src++);
137                                 FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
138                                 d0 = d1;
139                                 dst++;
140                                 n -= bits - dst_idx;
141                         }
142
143                         // Main chunk
144                         m = n % bits;
145                         n /= bits;
146                         while (n >= 4) {
147                                 d1 = FB_READL(src++);
148                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
149                                 d0 = d1;
150                                 d1 = FB_READL(src++);
151                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
152                                 d0 = d1;
153                                 d1 = FB_READL(src++);
154                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
155                                 d0 = d1;
156                                 d1 = FB_READL(src++);
157                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
158                                 d0 = d1;
159                                 n -= 4;
160                         }
161                         while (n--) {
162                                 d1 = FB_READL(src++);
163                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
164                                 d0 = d1;
165                         }
166
167                         // Trailing bits
168                         if (last) {
169                                 if (m <= right) {
170                                         // Single source word
171                                         FB_WRITEL( comp(d0 << left, FB_READL(dst), last), dst);
172                                 } else {
173                                         // 2 source words
174                                         d1 = FB_READL(src);
175                                         FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), last), dst);
176                                 }
177                         }
178                 }
179         }
180 }
181
182     /*
183      *  Generic bitwise copy algorithm, operating backward
184      */
185
186 static void
187 bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
188                 int src_idx, int bits, unsigned n)
189 {
190         unsigned long first, last;
191         int shift;
192
193         dst += (n-1)/bits;
194         src += (n-1)/bits;
195         if ((n-1) % bits) {
196                 dst_idx += (n-1) % bits;
197                 dst += dst_idx >> (ffs(bits) - 1);
198                 dst_idx &= bits - 1;
199                 src_idx += (n-1) % bits;
200                 src += src_idx >> (ffs(bits) - 1);
201                 src_idx &= bits - 1;
202         }
203
204         shift = dst_idx-src_idx;
205
206         first = FB_SHIFT_LOW(~0UL, bits - 1 - dst_idx);
207         last = ~(FB_SHIFT_LOW(~0UL, bits - 1 - ((dst_idx-n) % bits)));
208
209         if (!shift) {
210                 // Same alignment for source and dest
211
212                 if ((unsigned long)dst_idx+1 >= n) {
213                         // Single word
214                         if (last)
215                                 first &= last;
216                         FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
217                 } else {
218                         // Multiple destination words
219
220                         // Leading bits
221                         if (first != ~0UL) {
222                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
223                                 dst--;
224                                 src--;
225                                 n -= dst_idx+1;
226                         }
227
228                         // Main chunk
229                         n /= bits;
230                         while (n >= 8) {
231                                 FB_WRITEL(FB_READL(src--), dst--);
232                                 FB_WRITEL(FB_READL(src--), dst--);
233                                 FB_WRITEL(FB_READL(src--), dst--);
234                                 FB_WRITEL(FB_READL(src--), dst--);
235                                 FB_WRITEL(FB_READL(src--), dst--);
236                                 FB_WRITEL(FB_READL(src--), dst--);
237                                 FB_WRITEL(FB_READL(src--), dst--);
238                                 FB_WRITEL(FB_READL(src--), dst--);
239                                 n -= 8;
240                         }
241                         while (n--)
242                                 FB_WRITEL(FB_READL(src--), dst--);
243
244                         // Trailing bits
245                         if (last)
246                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
247                 }
248         } else {
249                 // Different alignment for source and dest
250
251                 int const left = -shift & (bits-1);
252                 int const right = shift & (bits-1);
253
254                 if ((unsigned long)dst_idx+1 >= n) {
255                         // Single destination word
256                         if (last)
257                                 first &= last;
258                         if (shift < 0) {
259                                 // Single source word
260                                 FB_WRITEL( comp( FB_READL(src)<<left, FB_READL(dst), first), dst);
261                         } else if (1+(unsigned long)src_idx >= n) {
262                                 // Single source word
263                                 FB_WRITEL( comp( FB_READL(src)>>right, FB_READL(dst), first), dst);
264                         } else {
265                                 // 2 source words
266                                 FB_WRITEL( comp( (FB_READL(src)>>right | FB_READL(src-1)<<left), FB_READL(dst), first), dst);
267                         }
268                 } else {
269                         // Multiple destination words
270                         /** We must always remember the last value read, because in case
271                         SRC and DST overlap bitwise (e.g. when moving just one pixel in
272                         1bpp), we always collect one full long for DST and that might
273                         overlap with the current long from SRC. We store this value in
274                         'd0'. */
275                         unsigned long d0, d1;
276                         int m;
277
278                         d0 = FB_READL(src--);
279                         // Leading bits
280                         if (shift < 0) {
281                                 // Single source word
282                                 FB_WRITEL( comp( (d0 << left), FB_READL(dst), first), dst);
283                         } else {
284                                 // 2 source words
285                                 d1 = FB_READL(src--);
286                                 FB_WRITEL( comp( (d0>>right | d1<<left), FB_READL(dst), first), dst);
287                                 d0 = d1;
288                         }
289                         dst--;
290                         n -= dst_idx+1;
291
292                         // Main chunk
293                         m = n % bits;
294                         n /= bits;
295                         while (n >= 4) {
296                                 d1 = FB_READL(src--);
297                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
298                                 d0 = d1;
299                                 d1 = FB_READL(src--);
300                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
301                                 d0 = d1;
302                                 d1 = FB_READL(src--);
303                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
304                                 d0 = d1;
305                                 d1 = FB_READL(src--);
306                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
307                                 d0 = d1;
308                                 n -= 4;
309                         }
310                         while (n--) {
311                                 d1 = FB_READL(src--);
312                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
313                                 d0 = d1;
314                         }
315
316                         // Trailing bits
317                         if (last) {
318                                 if (m <= left) {
319                                         // Single source word
320                                         FB_WRITEL( comp(d0 >> right, FB_READL(dst), last), dst);
321                                 } else {
322                                         // 2 source words
323                                         d1 = FB_READL(src);
324                                         FB_WRITEL( comp(d0>>right | d1<<left, FB_READL(dst), last), dst);
325                                 }
326                         }
327                 }
328         }
329 }
330
331 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
332 {
333         u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
334         u32 height = area->height, width = area->width;
335         unsigned long const bits_per_line = p->fix.line_length*8u;
336         unsigned long __iomem *dst = NULL, *src = NULL;
337         int bits = BITS_PER_LONG, bytes = bits >> 3;
338         int dst_idx = 0, src_idx = 0, rev_copy = 0;
339
340         if (p->state != FBINFO_STATE_RUNNING)
341                 return;
342
343         /* if the beginning of the target area might overlap with the end of
344         the source area, be have to copy the area reverse. */
345         if ((dy == sy && dx > sx) || (dy > sy)) {
346                 dy += height;
347                 sy += height;
348                 rev_copy = 1;
349         }
350
351         // split the base of the framebuffer into a long-aligned address and the
352         // index of the first bit
353         dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
354         dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
355         // add offset of source and target area
356         dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
357         src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
358
359         if (p->fbops->fb_sync)
360                 p->fbops->fb_sync(p);
361
362         if (rev_copy) {
363                 while (height--) {
364                         dst_idx -= bits_per_line;
365                         src_idx -= bits_per_line;
366                         dst += dst_idx >> (ffs(bits) - 1);
367                         dst_idx &= (bytes - 1);
368                         src += src_idx >> (ffs(bits) - 1);
369                         src_idx &= (bytes - 1);
370                         bitcpy_rev(dst, dst_idx, src, src_idx, bits,
371                                 width*p->var.bits_per_pixel);
372                 }
373         } else {
374                 while (height--) {
375                         dst += dst_idx >> (ffs(bits) - 1);
376                         dst_idx &= (bytes - 1);
377                         src += src_idx >> (ffs(bits) - 1);
378                         src_idx &= (bytes - 1);
379                         bitcpy(dst, dst_idx, src, src_idx, bits,
380                                 width*p->var.bits_per_pixel);
381                         dst_idx += bits_per_line;
382                         src_idx += bits_per_line;
383                 }
384         }
385 }
386
387 EXPORT_SYMBOL(cfb_copyarea);
388
389 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
390 MODULE_DESCRIPTION("Generic software accelerated copyarea");
391 MODULE_LICENSE("GPL");
392