Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
[linux-2.6] / drivers / video / syscopyarea.c
1 /*
2  *  Generic Bit Block Transfer for frame buffers located in system RAM with
3  *  packed pixels of any depth.
4  *
5  *  Based almost entirely from cfbcopyarea.c (which is based almost entirely
6  *  on Geert Uytterhoeven's copyarea routine)
7  *
8  *      Copyright (C)  2007 Antonino Daplas <adaplas@pol.net>
9  *
10  *  This file is subject to the terms and conditions of the GNU General Public
11  *  License.  See the file COPYING in the main directory of this archive for
12  *  more details.
13  *
14  */
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <linux/fb.h>
19 #include <linux/slab.h>
20 #include <asm/types.h>
21 #include <asm/io.h>
22 #include "fb_draw.h"
23
24     /*
25      *  Generic bitwise copy algorithm
26      */
27
28 static void
29 bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx,
30                 const unsigned long *src, int src_idx, int bits, unsigned n)
31 {
32         unsigned long first, last;
33         int const shift = dst_idx-src_idx;
34         int left, right;
35
36         first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
37         last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
38
39         if (!shift) {
40                 /* Same alignment for source and dest */
41                 if (dst_idx+n <= bits) {
42                         /* Single word */
43                         if (last)
44                                 first &= last;
45                         *dst = comp(*src, *dst, first);
46                 } else {
47                         /* Multiple destination words */
48                         /* Leading bits */
49                         if (first != ~0UL) {
50                                 *dst = comp(*src, *dst, first);
51                                 dst++;
52                                 src++;
53                                 n -= bits - dst_idx;
54                         }
55
56                         /* Main chunk */
57                         n /= bits;
58                         while (n >= 8) {
59                                 *dst++ = *src++;
60                                 *dst++ = *src++;
61                                 *dst++ = *src++;
62                                 *dst++ = *src++;
63                                 *dst++ = *src++;
64                                 *dst++ = *src++;
65                                 *dst++ = *src++;
66                                 *dst++ = *src++;
67                                 n -= 8;
68                         }
69                         while (n--)
70                                 *dst++ = *src++;
71
72                         /* Trailing bits */
73                         if (last)
74                                 *dst = comp(*src, *dst, last);
75                 }
76         } else {
77                 unsigned long d0, d1;
78                 int m;
79
80                 /* Different alignment for source and dest */
81                 right = shift & (bits - 1);
82                 left = -shift & (bits - 1);
83
84                 if (dst_idx+n <= bits) {
85                         /* Single destination word */
86                         if (last)
87                                 first &= last;
88                         if (shift > 0) {
89                                 /* Single source word */
90                                 *dst = comp(*src >> right, *dst, first);
91                         } else if (src_idx+n <= bits) {
92                                 /* Single source word */
93                                 *dst = comp(*src << left, *dst, first);
94                         } else {
95                                 /* 2 source words */
96                                 d0 = *src++;
97                                 d1 = *src;
98                                 *dst = comp(d0 << left | d1 >> right, *dst,
99                                             first);
100                         }
101                 } else {
102                         /* Multiple destination words */
103                         /** We must always remember the last value read,
104                             because in case SRC and DST overlap bitwise (e.g.
105                             when moving just one pixel in 1bpp), we always
106                             collect one full long for DST and that might
107                             overlap with the current long from SRC. We store
108                             this value in 'd0'. */
109                         d0 = *src++;
110                         /* Leading bits */
111                         if (shift > 0) {
112                                 /* Single source word */
113                                 *dst = comp(d0 >> right, *dst, first);
114                                 dst++;
115                                 n -= bits - dst_idx;
116                         } else {
117                                 /* 2 source words */
118                                 d1 = *src++;
119                                 *dst = comp(d0 << left | *dst >> right, *dst, first);
120                                 d0 = d1;
121                                 dst++;
122                                 n -= bits - dst_idx;
123                         }
124
125                         /* Main chunk */
126                         m = n % bits;
127                         n /= bits;
128                         while (n >= 4) {
129                                 d1 = *src++;
130                                 *dst++ = d0 << left | d1 >> right;
131                                 d0 = d1;
132                                 d1 = *src++;
133                                 *dst++ = d0 << left | d1 >> right;
134                                 d0 = d1;
135                                 d1 = *src++;
136                                 *dst++ = d0 << left | d1 >> right;
137                                 d0 = d1;
138                                 d1 = *src++;
139                                 *dst++ = d0 << left | d1 >> right;
140                                 d0 = d1;
141                                 n -= 4;
142                         }
143                         while (n--) {
144                                 d1 = *src++;
145                                 *dst++ = d0 << left | d1 >> right;
146                                 d0 = d1;
147                         }
148
149                         /* Trailing bits */
150                         if (last) {
151                                 if (m <= right) {
152                                         /* Single source word */
153                                         *dst = comp(d0 << left, *dst, last);
154                                 } else {
155                                         /* 2 source words */
156                                         d1 = *src;
157                                         *dst = comp(d0 << left | d1 >> right,
158                                                     *dst, last);
159                                 }
160                         }
161                 }
162         }
163 }
164
165     /*
166      *  Generic bitwise copy algorithm, operating backward
167      */
168
169 static void
170 bitcpy_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
171                 const unsigned long *src, int src_idx, int bits, unsigned n)
172 {
173         unsigned long first, last;
174         int shift;
175
176         dst += (n-1)/bits;
177         src += (n-1)/bits;
178         if ((n-1) % bits) {
179                 dst_idx += (n-1) % bits;
180                 dst += dst_idx >> (ffs(bits) - 1);
181                 dst_idx &= bits - 1;
182                 src_idx += (n-1) % bits;
183                 src += src_idx >> (ffs(bits) - 1);
184                 src_idx &= bits - 1;
185         }
186
187         shift = dst_idx-src_idx;
188
189         first = FB_SHIFT_LOW(p, ~0UL, bits - 1 - dst_idx);
190         last = ~(FB_SHIFT_LOW(p, ~0UL, bits - 1 - ((dst_idx-n) % bits)));
191
192         if (!shift) {
193                 /* Same alignment for source and dest */
194                 if ((unsigned long)dst_idx+1 >= n) {
195                         /* Single word */
196                         if (last)
197                                 first &= last;
198                         *dst = comp(*src, *dst, first);
199                 } else {
200                         /* Multiple destination words */
201
202                         /* Leading bits */
203                         if (first != ~0UL) {
204                                 *dst = comp(*src, *dst, first);
205                                 dst--;
206                                 src--;
207                                 n -= dst_idx+1;
208                         }
209
210                         /* Main chunk */
211                         n /= bits;
212                         while (n >= 8) {
213                                 *dst-- = *src--;
214                                 *dst-- = *src--;
215                                 *dst-- = *src--;
216                                 *dst-- = *src--;
217                                 *dst-- = *src--;
218                                 *dst-- = *src--;
219                                 *dst-- = *src--;
220                                 *dst-- = *src--;
221                                 n -= 8;
222                         }
223                         while (n--)
224                                 *dst-- = *src--;
225                         /* Trailing bits */
226                         if (last)
227                                 *dst = comp(*src, *dst, last);
228                 }
229         } else {
230                 /* Different alignment for source and dest */
231
232                 int const left = -shift & (bits-1);
233                 int const right = shift & (bits-1);
234
235                 if ((unsigned long)dst_idx+1 >= n) {
236                         /* Single destination word */
237                         if (last)
238                                 first &= last;
239                         if (shift < 0) {
240                                 /* Single source word */
241                                 *dst = comp(*src << left, *dst, first);
242                         } else if (1+(unsigned long)src_idx >= n) {
243                                 /* Single source word */
244                                 *dst = comp(*src >> right, *dst, first);
245                         } else {
246                                 /* 2 source words */
247                                 *dst = comp(*src >> right | *(src-1) << left,
248                                             *dst, first);
249                         }
250                 } else {
251                         /* Multiple destination words */
252                         /** We must always remember the last value read,
253                             because in case SRC and DST overlap bitwise (e.g.
254                             when moving just one pixel in 1bpp), we always
255                             collect one full long for DST and that might
256                             overlap with the current long from SRC. We store
257                             this value in 'd0'. */
258                         unsigned long d0, d1;
259                         int m;
260
261                         d0 = *src--;
262                         /* Leading bits */
263                         if (shift < 0) {
264                                 /* Single source word */
265                                 *dst = comp(d0 << left, *dst, first);
266                         } else {
267                                 /* 2 source words */
268                                 d1 = *src--;
269                                 *dst = comp(d0 >> right | d1 << left, *dst,
270                                             first);
271                                 d0 = d1;
272                         }
273                         dst--;
274                         n -= dst_idx+1;
275
276                         /* Main chunk */
277                         m = n % bits;
278                         n /= bits;
279                         while (n >= 4) {
280                                 d1 = *src--;
281                                 *dst-- = d0 >> right | d1 << left;
282                                 d0 = d1;
283                                 d1 = *src--;
284                                 *dst-- = d0 >> right | d1 << left;
285                                 d0 = d1;
286                                 d1 = *src--;
287                                 *dst-- = d0 >> right | d1 << left;
288                                 d0 = d1;
289                                 d1 = *src--;
290                                 *dst-- = d0 >> right | d1 << left;
291                                 d0 = d1;
292                                 n -= 4;
293                         }
294                         while (n--) {
295                                 d1 = *src--;
296                                 *dst-- = d0 >> right | d1 << left;
297                                 d0 = d1;
298                         }
299
300                         /* Trailing bits */
301                         if (last) {
302                                 if (m <= left) {
303                                         /* Single source word */
304                                         *dst = comp(d0 >> right, *dst, last);
305                                 } else {
306                                         /* 2 source words */
307                                         d1 = *src;
308                                         *dst = comp(d0 >> right | d1 << left,
309                                                     *dst, last);
310                                 }
311                         }
312                 }
313         }
314 }
315
316 void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area)
317 {
318         u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
319         u32 height = area->height, width = area->width;
320         unsigned long const bits_per_line = p->fix.line_length*8u;
321         unsigned long *dst = NULL, *src = NULL;
322         int bits = BITS_PER_LONG, bytes = bits >> 3;
323         int dst_idx = 0, src_idx = 0, rev_copy = 0;
324
325         if (p->state != FBINFO_STATE_RUNNING)
326                 return;
327
328         /* if the beginning of the target area might overlap with the end of
329         the source area, be have to copy the area reverse. */
330         if ((dy == sy && dx > sx) || (dy > sy)) {
331                 dy += height;
332                 sy += height;
333                 rev_copy = 1;
334         }
335
336         /* split the base of the framebuffer into a long-aligned address and
337            the index of the first bit */
338         dst = src = (unsigned long *)((unsigned long)p->screen_base &
339                                       ~(bytes-1));
340         dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
341         /* add offset of source and target area */
342         dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
343         src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
344
345         if (p->fbops->fb_sync)
346                 p->fbops->fb_sync(p);
347
348         if (rev_copy) {
349                 while (height--) {
350                         dst_idx -= bits_per_line;
351                         src_idx -= bits_per_line;
352                         dst += dst_idx >> (ffs(bits) - 1);
353                         dst_idx &= (bytes - 1);
354                         src += src_idx >> (ffs(bits) - 1);
355                         src_idx &= (bytes - 1);
356                         bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
357                                 width*p->var.bits_per_pixel);
358                 }
359         } else {
360                 while (height--) {
361                         dst += dst_idx >> (ffs(bits) - 1);
362                         dst_idx &= (bytes - 1);
363                         src += src_idx >> (ffs(bits) - 1);
364                         src_idx &= (bytes - 1);
365                         bitcpy(p, dst, dst_idx, src, src_idx, bits,
366                                 width*p->var.bits_per_pixel);
367                         dst_idx += bits_per_line;
368                         src_idx += bits_per_line;
369                 }
370         }
371 }
372
373 EXPORT_SYMBOL(sys_copyarea);
374
375 MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
376 MODULE_DESCRIPTION("Generic copyarea (sys-to-sys)");
377 MODULE_LICENSE("GPL");
378