ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / video / cfbcopyarea.c
1 /*
2  *  Generic function for frame buffer with packed pixels of any depth.
3  *
4  *      Copyright (C)  June 1999 James Simmons
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  *  The code for 24 bit is horrible. It copies byte by byte size instead of 
17  *  longs like the other sizes. Needs to be optimized. 
18  *
19  *  Also need to add code to deal with cards endians that are different than 
20  *  the native cpu endians. I also need to deal with MSB position in the word.
21  *  
22  */
23 #include <linux/config.h>
24 #include <linux/module.h>
25 #include <linux/kernel.h>
26 #include <linux/string.h>
27 #include <linux/fb.h>
28 #include <linux/slab.h>
29 #include <asm/types.h>
30 #include <asm/io.h>
31
32 #define LONG_MASK  (BITS_PER_LONG - 1)
33
34 #if BITS_PER_LONG == 32
35 #define FB_WRITEL fb_writel
36 #define FB_READL  fb_readl
37 #define SHIFT_PER_LONG 5
38 #define BYTES_PER_LONG 4
39 #else
40 #define FB_WRITEL fb_writeq
41 #define FB_READL  fb_readq
42 #define SHIFT_PER_LONG 6
43 #define BYTES_PER_LONG 8
44 #endif
45
46 static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
47                    int src_idx, unsigned long n)
48 {
49         unsigned long first, last;
50         int shift = dst_idx-src_idx, left, right;
51         unsigned long d0, d1;
52         int m;
53         
54         if (!n)
55                 return;
56         
57         shift = dst_idx-src_idx;
58         first = ~0UL >> dst_idx;
59         last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
60         
61         if (!shift) {
62                 // Same alignment for source and dest
63                 
64                 if (dst_idx+n <= BITS_PER_LONG) {
65                         // Single word
66                         if (last)
67                                 first &= last;
68                         FB_WRITEL((FB_READL(src) & first) | (FB_READL(dst) & ~first), dst);
69                 } else {
70                         // Multiple destination words
71                         // Leading bits
72                         if (first) {
73                                 
74                                 FB_WRITEL((FB_READL(src) & first) | 
75                                           (FB_READL(dst) & ~first), dst);
76                                 dst++;
77                                 src++;
78                                 n -= BITS_PER_LONG-dst_idx;
79                         }
80                         
81                         // Main chunk
82                         n /= BITS_PER_LONG;
83                         while (n >= 8) {
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                                 FB_WRITEL(FB_READL(src++), dst++);
89                                 FB_WRITEL(FB_READL(src++), dst++);
90                                 FB_WRITEL(FB_READL(src++), dst++);
91                                 FB_WRITEL(FB_READL(src++), dst++);
92                                 n -= 8;
93                         }
94                         while (n--)
95                                 FB_WRITEL(FB_READL(src++), dst++);
96                         // Trailing bits
97                         if (last)
98                                 FB_WRITEL((FB_READL(src) & last) | (FB_READL(dst) & ~last), dst);
99                 }
100         } else {
101                 // Different alignment for source and dest
102                 
103                 right = shift & (BITS_PER_LONG-1);
104                 left = -shift & (BITS_PER_LONG-1);
105                 
106                 if (dst_idx+n <= BITS_PER_LONG) {
107                         // Single destination word
108                         if (last)
109                                 first &= last;
110                         if (shift > 0) {
111                                 // Single source word
112                                 FB_WRITEL(((FB_READL(src) >> right) & first) | 
113                                           (FB_READL(dst) & ~first), dst);
114                         } else if (src_idx+n <= BITS_PER_LONG) {
115                                 // Single source word
116                                 FB_WRITEL(((FB_READL(src) << left) & first) | 
117                                           (FB_READL(dst) & ~first), dst);
118                         } else {
119                                 // 2 source words
120                                 d0 = FB_READL(src++);
121                                 d1 = FB_READL(src);
122                                 FB_WRITEL(((d0<<left | d1>>right) & first) | 
123                                           (FB_READL(dst) & ~first), dst);
124                         }
125                 } else {
126                         // Multiple destination words
127                         d0 = FB_READL(src++);
128                         // Leading bits
129                         if (shift > 0) {
130                                 // Single source word
131                                 FB_WRITEL(((d0 >> right) & first) | 
132                                           (FB_READL(dst) & ~first), dst);
133                                 dst++;
134                                 n -= BITS_PER_LONG-dst_idx;
135                         } else {
136                                 // 2 source words
137                                 d1 = FB_READL(src++);
138                                 FB_WRITEL(((d0<<left | d1>>right) & first) | 
139                                           (FB_READL(dst) & ~first), dst);
140                                 d0 = d1;
141                                 dst++;
142                                 n -= BITS_PER_LONG-dst_idx;
143                         }
144                         
145                         // Main chunk
146                         m = n % BITS_PER_LONG;
147                         n /= BITS_PER_LONG;
148                         while (n >= 4) {
149                                 d1 = FB_READL(src++);
150                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
151                                 d0 = d1;
152                                 d1 = FB_READL(src++);
153                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
154                                 d0 = d1;
155                                 d1 = FB_READL(src++);
156                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
157                                 d0 = d1;
158                                 d1 = FB_READL(src++);
159                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
160                                 d0 = d1;
161                                 n -= 4;
162                         }
163                         while (n--) {
164                                 d1 = FB_READL(src++);
165                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
166                                 d0 = d1;
167                         }
168                         
169                         // Trailing bits
170                         if (last) {
171                                 if (m <= right) {
172                                         // Single source word
173                                         FB_WRITEL(((d0 << left) & last) | 
174                                                   (FB_READL(dst) & ~last), 
175                                                   dst);
176                                 } else {
177                                         // 2 source words
178                                         d1 = FB_READL(src);
179                                         FB_WRITEL(((d0<<left | d1>>right) & 
180                                                    last) | (FB_READL(dst) & 
181                                                             ~last), dst);
182                                 }
183                         }
184                 }
185         }
186 }
187
188 static void bitcpy_rev(unsigned long *dst, int dst_idx,
189                        const unsigned long *src, int src_idx, unsigned long n)
190 {
191         unsigned long first, last;
192         int shift = dst_idx-src_idx, left, right;
193         unsigned long d0, d1;
194         int m;
195         
196         if (!n)
197                 return;
198         
199         dst += (n-1)/BITS_PER_LONG;
200         src += (n-1)/BITS_PER_LONG;
201         if ((n-1) % BITS_PER_LONG) {
202                 dst_idx += (n-1) % BITS_PER_LONG;
203                 dst += dst_idx >> SHIFT_PER_LONG;
204                 dst_idx &= BITS_PER_LONG-1;
205                 src_idx += (n-1) % BITS_PER_LONG;
206                 src += src_idx >> SHIFT_PER_LONG;
207                 src_idx &= BITS_PER_LONG-1;
208         }
209         
210         shift = dst_idx-src_idx;
211         first = ~0UL << (BITS_PER_LONG-1-dst_idx);
212         last = ~(~0UL << (BITS_PER_LONG-1-((dst_idx-n) % BITS_PER_LONG)));
213         
214         if (!shift) {
215                 // Same alignment for source and dest
216                 
217                 if ((unsigned long)dst_idx+1 >= n) {
218                         // Single word
219                         if (last)
220                                 first &= last;
221                         FB_WRITEL((FB_READL(src) & first) | (FB_READL(dst) & ~first), dst); 
222                 } else {
223                         // Multiple destination words
224                         // Leading bits
225                         if (first) {
226                                 FB_WRITEL((FB_READL(src) & first) | (FB_READL(dst) & ~first), dst); 
227                                 dst--;
228                                 src--;
229                                 n -= dst_idx+1;
230                         }
231                         
232                         // Main chunk
233                         n /= BITS_PER_LONG;
234                         while (n >= 8) {
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                                 FB_WRITEL(FB_READL(src--), dst--);
240                                 FB_WRITEL(FB_READL(src--), dst--);
241                                 FB_WRITEL(FB_READL(src--), dst--);
242                                 FB_WRITEL(FB_READL(src--), dst--);
243                                 n -= 8;
244                         }
245                         while (n--)
246                                 FB_WRITEL(FB_READL(src--), dst--);
247                         
248                         // Trailing bits
249                         if (last)
250                                 FB_WRITEL((FB_READL(src) & last) | (FB_READL(dst) & ~last), dst);
251                 }
252         } else {
253                 // Different alignment for source and dest
254                 
255                 right = shift & (BITS_PER_LONG-1);
256                 left = -shift & (BITS_PER_LONG-1);
257                 
258                 if ((unsigned long)dst_idx+1 >= n) {
259                         // Single destination word
260                         if (last)
261                                 first &= last;
262                         if (shift < 0) {
263                                 // Single source word
264                                 FB_WRITEL((FB_READL(src) << left & first) | 
265                                           (FB_READL(dst) & ~first), dst);
266                         } else if (1+(unsigned long)src_idx >= n) {
267                                 // Single source word
268                                 FB_WRITEL(((FB_READL(src) >> right) & first) | 
269                                           (FB_READL(dst) & ~first), dst);
270                         } else {
271                                 // 2 source words
272                                 d0 = FB_READL(src--);
273                                 d1 = FB_READL(src);
274                                 FB_WRITEL(((d0>>right | d1<<left) & first) | 
275                                           (FB_READL(dst) & ~first), dst);
276                         }
277                 } else {
278                         // Multiple destination words
279                         d0 = FB_READL(src--);
280                         // Leading bits
281                         if (shift < 0) {
282                                 // Single source word
283                                 FB_WRITEL(((d0 << left) & first) | 
284                                           (FB_READL(dst) & ~first), dst);
285                                 dst--;
286                                 n -= dst_idx+1;
287                         } else {
288                                 // 2 source words
289                                 d1 = FB_READL(src--);
290                                 FB_WRITEL(((d0>>right | d1<<left) & first) | 
291                                           (FB_READL(dst) & ~first), dst);
292                                 d0 = d1;
293                                 dst--;
294                                 n -= dst_idx+1;
295                         }
296                         
297                         // Main chunk
298                         m = n % BITS_PER_LONG;
299                         n /= BITS_PER_LONG;
300                         while (n >= 4) {
301                                 d1 = FB_READL(src--);
302                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
303                                 d0 = d1;
304                                 d1 = FB_READL(src--);
305                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
306                                 d0 = d1;
307                                 d1 = FB_READL(src--);
308                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
309                                 d0 = d1;
310                                 d1 = FB_READL(src--);
311                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
312                                 d0 = d1;
313                                 n -= 4;
314                         }
315                         while (n--) {
316                                 d1 = FB_READL(src--);
317                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
318                                 d0 = d1;
319                         }
320                         
321                         // Trailing bits
322                         if (last) {
323                                 if (m <= left) {
324                                         // Single source word
325                                         FB_WRITEL(((d0 >> right) & last) | 
326                                                   (FB_READL(dst) & ~last), 
327                                                   dst);
328                                 } else {
329                                         // 2 source words
330                                         d1 = FB_READL(src);
331                                         FB_WRITEL(((d0>>right | d1<<left) & 
332                                                    last) | (FB_READL(dst) & 
333                                                             ~last), dst);
334                                 }
335                         }
336                 }
337         }
338 }
339
340 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
341 {
342         u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
343         u32 height = area->height, width = area->width;
344         int x2, y2, old_dx, old_dy, vxres, vyres;
345         unsigned long next_line = p->fix.line_length;
346         int dst_idx = 0, src_idx = 0, rev_copy = 0;
347         unsigned long *dst = NULL, *src = NULL;
348
349         if (p->state != FBINFO_STATE_RUNNING)
350                 return;
351
352         /* We want rotation but lack hardware to do it for us. */
353         if (!p->fbops->fb_rotate && p->var.rotate) {
354         }       
355         
356         vxres = p->var.xres_virtual;
357         vyres = p->var.yres_virtual;
358
359         if (area->dx > vxres || area->sx > vxres || 
360             area->dy > vyres || area->sy > vyres)
361                 return;
362
363         /* clip the destination */
364         old_dx = area->dx;
365         old_dy = area->dy;
366
367         /*
368          * We could use hardware clipping but on many cards you get around
369          * hardware clipping by writing to framebuffer directly.
370          */
371         x2 = area->dx + area->width;
372         y2 = area->dy + area->height;
373         dx = area->dx > 0 ? area->dx : 0;
374         dy = area->dy > 0 ? area->dy : 0;
375         x2 = x2 < vxres ? x2 : vxres;
376         y2 = y2 < vyres ? y2 : vyres;
377         width = x2 - dx;
378         height = y2 - dy;
379
380         /* update sx1,sy1 */
381         sx += (dx - old_dx);
382         sy += (dy - old_dy);
383
384         /* the source must be completely inside the virtual screen */
385         if (sx < 0 || sy < 0 ||
386             (sx + width) > vxres ||
387             (sy + height) > vyres)
388                 return;
389
390         if ((dy == sy && dx > sx) ||    
391             (dy > sy)) { 
392                 dy += height;
393                 sy += height;
394                 rev_copy = 1;
395         }
396
397         dst = src = (unsigned long *)((unsigned long)p->screen_base & 
398                                       ~(BYTES_PER_LONG-1));
399         dst_idx = src_idx = (unsigned long)p->screen_base & (BYTES_PER_LONG-1);
400         dst_idx += dy*next_line*8 + dx*p->var.bits_per_pixel;
401         src_idx += sy*next_line*8 + sx*p->var.bits_per_pixel;
402         
403         if (p->fbops->fb_sync)
404                 p->fbops->fb_sync(p);
405         if (rev_copy) {
406                 while (height--) {
407                         dst_idx -= next_line*8;
408                         src_idx -= next_line*8;
409                         dst += dst_idx >> SHIFT_PER_LONG;
410                         dst_idx &= (BYTES_PER_LONG-1);
411                         src += src_idx >> SHIFT_PER_LONG;
412                         src_idx &= (BYTES_PER_LONG-1);
413                         bitcpy_rev(dst, dst_idx, src, src_idx, 
414                                    width*p->var.bits_per_pixel);
415                 }       
416         } else {
417                 while (height--) {
418                         dst += dst_idx >> SHIFT_PER_LONG;
419                         dst_idx &= (BYTES_PER_LONG-1);
420                         src += src_idx >> SHIFT_PER_LONG;
421                         src_idx &= (BYTES_PER_LONG-1);
422                         bitcpy(dst, dst_idx, src, src_idx, 
423                                width*p->var.bits_per_pixel);
424                         dst_idx += next_line*8;
425                         src_idx += next_line*8;
426                 }       
427         }
428 }
429
430 EXPORT_SYMBOL(cfb_copyarea);
431
432 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
433 MODULE_DESCRIPTION("Generic software accelerated copyarea");
434 MODULE_LICENSE("GPL");
435