[PATCH] sg traverse fix for __atapi_pio_bytes()
[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
26
27 #include <linux/config.h>
28 #include <linux/module.h>
29 #include <linux/kernel.h>
30 #include <linux/string.h>
31 #include <linux/fb.h>
32 #include <linux/slab.h>
33 #include <asm/types.h>
34 #include <asm/io.h>
35
36 #if BITS_PER_LONG == 32
37 #  define FB_WRITEL fb_writel
38 #  define FB_READL  fb_readl
39 #else
40 #  define FB_WRITEL fb_writeq
41 #  define FB_READL  fb_readq
42 #endif
43
44     /*
45      *  Compose two values, using a bitmask as decision value
46      *  This is equivalent to (a & mask) | (b & ~mask)
47      */
48
49 static inline unsigned long
50 comp(unsigned long a, unsigned long b, unsigned long mask)
51 {
52     return ((a ^ b) & mask) ^ b;
53 }
54
55     /*
56      *  Generic bitwise copy algorithm
57      */
58
59 static void
60 bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
61         int src_idx, int bits, unsigned n)
62 {
63         unsigned long first, last;
64         int const shift = dst_idx-src_idx;
65         int left, right;
66
67         first = ~0UL >> dst_idx;
68         last = ~(~0UL >> ((dst_idx+n) % bits));
69
70         if (!shift) {
71                 // Same alignment for source and dest
72
73                 if (dst_idx+n <= bits) {
74                         // Single word
75                         if (last)
76                                 first &= last;
77                         FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
78                 } else {
79                         // Multiple destination words
80
81                         // Leading bits
82                         if (first != ~0UL) {
83                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
84                                 dst++;
85                                 src++;
86                                 n -= bits - dst_idx;
87                         }
88
89                         // Main chunk
90                         n /= bits;
91                         while (n >= 8) {
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                                 FB_WRITEL(FB_READL(src++), dst++);
100                                 n -= 8;
101                         }
102                         while (n--)
103                                 FB_WRITEL(FB_READL(src++), dst++);
104
105                         // Trailing bits
106                         if (last)
107                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
108                 }
109         } else {
110                 unsigned long d0, d1;
111                 int m;
112                 // Different alignment for source and dest
113
114                 right = shift & (bits - 1);
115                 left = -shift & (bits - 1);
116
117                 if (dst_idx+n <= bits) {
118                         // Single destination word
119                         if (last)
120                                 first &= last;
121                         if (shift > 0) {
122                                 // Single source word
123                                 FB_WRITEL( comp( FB_READL(src) >> right, FB_READL(dst), first), dst);
124                         } else if (src_idx+n <= bits) {
125                                 // Single source word
126                                 FB_WRITEL( comp(FB_READL(src) << left, FB_READL(dst), first), dst);
127                         } else {
128                                 // 2 source words
129                                 d0 = FB_READL(src++);
130                                 d1 = FB_READL(src);
131                                 FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
132                         }
133                 } else {
134                         // Multiple destination words
135                         /** We must always remember the last value read, because in case
136                         SRC and DST overlap bitwise (e.g. when moving just one pixel in
137                         1bpp), we always collect one full long for DST and that might
138                         overlap with the current long from SRC. We store this value in
139                         'd0'. */
140                         d0 = FB_READL(src++);
141                         // Leading bits
142                         if (shift > 0) {
143                                 // Single source word
144                                 FB_WRITEL( comp(d0 >> right, FB_READL(dst), first), dst);
145                                 dst++;
146                                 n -= bits - dst_idx;
147                         } else {
148                                 // 2 source words
149                                 d1 = FB_READL(src++);
150                                 FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
151                                 d0 = d1;
152                                 dst++;
153                                 n -= bits - dst_idx;
154                         }
155
156                         // Main chunk
157                         m = n % bits;
158                         n /= bits;
159                         while (n >= 4) {
160                                 d1 = FB_READL(src++);
161                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
162                                 d0 = d1;
163                                 d1 = FB_READL(src++);
164                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
165                                 d0 = d1;
166                                 d1 = FB_READL(src++);
167                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
168                                 d0 = d1;
169                                 d1 = FB_READL(src++);
170                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
171                                 d0 = d1;
172                                 n -= 4;
173                         }
174                         while (n--) {
175                                 d1 = FB_READL(src++);
176                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
177                                 d0 = d1;
178                         }
179
180                         // Trailing bits
181                         if (last) {
182                                 if (m <= right) {
183                                         // Single source word
184                                         FB_WRITEL( comp(d0 << left, FB_READL(dst), last), dst);
185                                 } else {
186                                         // 2 source words
187                                         d1 = FB_READL(src);
188                                         FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), last), dst);
189                                 }
190                         }
191                 }
192         }
193 }
194
195     /*
196      *  Generic bitwise copy algorithm, operating backward
197      */
198
199 static void
200 bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
201                 int src_idx, int bits, unsigned n)
202 {
203         unsigned long first, last;
204         int shift;
205
206         dst += (n-1)/bits;
207         src += (n-1)/bits;
208         if ((n-1) % bits) {
209                 dst_idx += (n-1) % bits;
210                 dst += dst_idx >> (ffs(bits) - 1);
211                 dst_idx &= bits - 1;
212                 src_idx += (n-1) % bits;
213                 src += src_idx >> (ffs(bits) - 1);
214                 src_idx &= bits - 1;
215         }
216
217         shift = dst_idx-src_idx;
218
219         first = ~0UL << (bits - 1 - dst_idx);
220         last = ~(~0UL << (bits - 1 - ((dst_idx-n) % bits)));
221
222         if (!shift) {
223                 // Same alignment for source and dest
224
225                 if ((unsigned long)dst_idx+1 >= n) {
226                         // Single word
227                         if (last)
228                                 first &= last;
229                         FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
230                 } else {
231                         // Multiple destination words
232
233                         // Leading bits
234                         if (first != ~0UL) {
235                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
236                                 dst--;
237                                 src--;
238                                 n -= dst_idx+1;
239                         }
240
241                         // Main chunk
242                         n /= bits;
243                         while (n >= 8) {
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                                 FB_WRITEL(FB_READL(src--), dst--);
252                                 n -= 8;
253                         }
254                         while (n--)
255                                 FB_WRITEL(FB_READL(src--), dst--);
256
257                         // Trailing bits
258                         if (last)
259                                 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
260                 }
261         } else {
262                 // Different alignment for source and dest
263
264                 int const left = -shift & (bits-1);
265                 int const right = shift & (bits-1);
266
267                 if ((unsigned long)dst_idx+1 >= n) {
268                         // Single destination word
269                         if (last)
270                                 first &= last;
271                         if (shift < 0) {
272                                 // Single source word
273                                 FB_WRITEL( comp( FB_READL(src)<<left, FB_READL(dst), first), dst);
274                         } else if (1+(unsigned long)src_idx >= n) {
275                                 // Single source word
276                                 FB_WRITEL( comp( FB_READL(src)>>right, FB_READL(dst), first), dst);
277                         } else {
278                                 // 2 source words
279                                 FB_WRITEL( comp( (FB_READL(src)>>right | FB_READL(src-1)<<left), FB_READL(dst), first), dst);
280                         }
281                 } else {
282                         // Multiple destination words
283                         /** We must always remember the last value read, because in case
284                         SRC and DST overlap bitwise (e.g. when moving just one pixel in
285                         1bpp), we always collect one full long for DST and that might
286                         overlap with the current long from SRC. We store this value in
287                         'd0'. */
288                         unsigned long d0, d1;
289                         int m;
290
291                         d0 = FB_READL(src--);
292                         // Leading bits
293                         if (shift < 0) {
294                                 // Single source word
295                                 FB_WRITEL( comp( (d0 << left), FB_READL(dst), first), dst);
296                         } else {
297                                 // 2 source words
298                                 d1 = FB_READL(src--);
299                                 FB_WRITEL( comp( (d0>>right | d1<<left), FB_READL(dst), first), dst);
300                                 d0 = d1;
301                         }
302                         dst--;
303                         n -= dst_idx+1;
304
305                         // Main chunk
306                         m = n % bits;
307                         n /= bits;
308                         while (n >= 4) {
309                                 d1 = FB_READL(src--);
310                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
311                                 d0 = d1;
312                                 d1 = FB_READL(src--);
313                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
314                                 d0 = d1;
315                                 d1 = FB_READL(src--);
316                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
317                                 d0 = d1;
318                                 d1 = FB_READL(src--);
319                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
320                                 d0 = d1;
321                                 n -= 4;
322                         }
323                         while (n--) {
324                                 d1 = FB_READL(src--);
325                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
326                                 d0 = d1;
327                         }
328
329                         // Trailing bits
330                         if (last) {
331                                 if (m <= left) {
332                                         // Single source word
333                                         FB_WRITEL( comp(d0 >> right, FB_READL(dst), last), dst);
334                                 } else {
335                                         // 2 source words
336                                         d1 = FB_READL(src);
337                                         FB_WRITEL( comp(d0>>right | d1<<left, FB_READL(dst), last), dst);
338                                 }
339                         }
340                 }
341         }
342 }
343
344 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
345 {
346         u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
347         u32 height = area->height, width = area->width;
348         unsigned long const bits_per_line = p->fix.line_length*8u;
349         unsigned long __iomem *dst = NULL, *src = NULL;
350         int bits = BITS_PER_LONG, bytes = bits >> 3;
351         int dst_idx = 0, src_idx = 0, rev_copy = 0;
352         int x2, y2, vxres, vyres;
353
354         if (p->state != FBINFO_STATE_RUNNING)
355                 return;
356
357         /* We want rotation but lack hardware to do it for us. */
358         if (!p->fbops->fb_rotate && p->var.rotate) {
359         }
360
361         vxres = p->var.xres_virtual;
362         vyres = p->var.yres_virtual;
363
364         if (area->dx > vxres || area->sx > vxres ||
365             area->dy > vyres || area->sy > vyres)
366                 return;
367
368         /* clip the destination
369          * We could use hardware clipping but on many cards you get around
370          * hardware clipping by writing to framebuffer directly.
371          */
372         x2 = area->dx + area->width;
373         y2 = area->dy + area->height;
374         dx = area->dx > 0 ? area->dx : 0;
375         dy = area->dy > 0 ? area->dy : 0;
376         x2 = x2 < vxres ? x2 : vxres;
377         y2 = y2 < vyres ? y2 : vyres;
378         width = x2 - dx;
379         height = y2 - dy;
380
381         if ((width==0) ||(height==0))
382                 return;
383
384         /* update sx1,sy1 */
385         sx += (dx - area->dx);
386         sy += (dy - area->dy);
387
388         /* the source must be completely inside the virtual screen */
389         if (sx < 0 || sy < 0 || (sx + width) > vxres || (sy + height) > vyres)
390                 return;
391
392         /* if the beginning of the target area might overlap with the end of
393         the source area, be have to copy the area reverse. */
394         if ((dy == sy && dx > sx) || (dy > sy)) {
395                 dy += height;
396                 sy += height;
397                 rev_copy = 1;
398         }
399
400         // split the base of the framebuffer into a long-aligned address and the
401         // index of the first bit
402         dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
403         dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
404         // add offset of source and target area
405         dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
406         src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
407
408         if (p->fbops->fb_sync)
409                 p->fbops->fb_sync(p);
410
411         if (rev_copy) {
412                 while (height--) {
413                         dst_idx -= bits_per_line;
414                         src_idx -= bits_per_line;
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_rev(dst, dst_idx, src, src_idx, bits,
420                                 width*p->var.bits_per_pixel);
421                 }
422         } else {
423                 while (height--) {
424                         dst += dst_idx >> (ffs(bits) - 1);
425                         dst_idx &= (bytes - 1);
426                         src += src_idx >> (ffs(bits) - 1);
427                         src_idx &= (bytes - 1);
428                         bitcpy(dst, dst_idx, src, src_idx, bits,
429                                 width*p->var.bits_per_pixel);
430                         dst_idx += bits_per_line;
431                         src_idx += bits_per_line;
432                 }
433         }
434 }
435
436 EXPORT_SYMBOL(cfb_copyarea);
437
438 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
439 MODULE_DESCRIPTION("Generic software accelerated copyarea");
440 MODULE_LICENSE("GPL");
441