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