vserver 1.9.3
[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 __iomem *dst, int dst_idx,
47                    const unsigned long __iomem *src, int src_idx,
48                    unsigned long n)
49 {
50         unsigned long first, last;
51         int shift = dst_idx-src_idx, left, right;
52         unsigned long d0, d1;
53         int m;
54         
55         if (!n)
56                 return;
57         
58         shift = dst_idx-src_idx;
59         first = ~0UL >> dst_idx;
60         last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
61         
62         if (!shift) {
63                 // Same alignment for source and dest
64                 
65                 if (dst_idx+n <= BITS_PER_LONG) {
66                         // Single word
67                         if (last)
68                                 first &= last;
69                         FB_WRITEL((FB_READL(src) & first) | (FB_READL(dst) & ~first), dst);
70                 } else {
71                         // Multiple destination words
72                         // Leading bits
73                         if (first) {
74                                 
75                                 FB_WRITEL((FB_READL(src) & first) | 
76                                           (FB_READL(dst) & ~first), dst);
77                                 dst++;
78                                 src++;
79                                 n -= BITS_PER_LONG-dst_idx;
80                         }
81                         
82                         // Main chunk
83                         n /= BITS_PER_LONG;
84                         while (n >= 8) {
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                                 FB_WRITEL(FB_READL(src++), dst++);
93                                 n -= 8;
94                         }
95                         while (n--)
96                                 FB_WRITEL(FB_READL(src++), dst++);
97                         // Trailing bits
98                         if (last)
99                                 FB_WRITEL((FB_READL(src) & last) | (FB_READL(dst) & ~last), dst);
100                 }
101         } else {
102                 // Different alignment for source and dest
103                 
104                 right = shift & (BITS_PER_LONG-1);
105                 left = -shift & (BITS_PER_LONG-1);
106                 
107                 if (dst_idx+n <= BITS_PER_LONG) {
108                         // Single destination word
109                         if (last)
110                                 first &= last;
111                         if (shift > 0) {
112                                 // Single source word
113                                 FB_WRITEL(((FB_READL(src) >> right) & first) | 
114                                           (FB_READL(dst) & ~first), dst);
115                         } else if (src_idx+n <= BITS_PER_LONG) {
116                                 // Single source word
117                                 FB_WRITEL(((FB_READL(src) << left) & first) | 
118                                           (FB_READL(dst) & ~first), dst);
119                         } else {
120                                 // 2 source words
121                                 d0 = FB_READL(src++);
122                                 d1 = FB_READL(src);
123                                 FB_WRITEL(((d0<<left | d1>>right) & first) | 
124                                           (FB_READL(dst) & ~first), dst);
125                         }
126                 } else {
127                         // Multiple destination words
128                         d0 = FB_READL(src++);
129                         // Leading bits
130                         if (shift > 0) {
131                                 // Single source word
132                                 FB_WRITEL(((d0 >> right) & first) | 
133                                           (FB_READL(dst) & ~first), dst);
134                                 dst++;
135                                 n -= BITS_PER_LONG-dst_idx;
136                         } else {
137                                 // 2 source words
138                                 d1 = FB_READL(src++);
139                                 FB_WRITEL(((d0<<left | d1>>right) & first) | 
140                                           (FB_READL(dst) & ~first), dst);
141                                 d0 = d1;
142                                 dst++;
143                                 n -= BITS_PER_LONG-dst_idx;
144                         }
145                         
146                         // Main chunk
147                         m = n % BITS_PER_LONG;
148                         n /= BITS_PER_LONG;
149                         while (n >= 4) {
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                                 d1 = FB_READL(src++);
160                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
161                                 d0 = d1;
162                                 n -= 4;
163                         }
164                         while (n--) {
165                                 d1 = FB_READL(src++);
166                                 FB_WRITEL(d0 << left | d1 >> right, dst++);
167                                 d0 = d1;
168                         }
169                         
170                         // Trailing bits
171                         if (last) {
172                                 if (m <= right) {
173                                         // Single source word
174                                         FB_WRITEL(((d0 << left) & last) | 
175                                                   (FB_READL(dst) & ~last), 
176                                                   dst);
177                                 } else {
178                                         // 2 source words
179                                         d1 = FB_READL(src);
180                                         FB_WRITEL(((d0<<left | d1>>right) & 
181                                                    last) | (FB_READL(dst) & 
182                                                             ~last), dst);
183                                 }
184                         }
185                 }
186         }
187 }
188
189 static void bitcpy_rev(unsigned long __iomem *dst, int dst_idx,
190                        const unsigned long __iomem *src, int src_idx, unsigned long n)
191 {
192         unsigned long first, last;
193         int shift = dst_idx-src_idx, left, right;
194         unsigned long d0, d1;
195         int m;
196         
197         if (!n)
198                 return;
199         
200         dst += (n-1)/BITS_PER_LONG;
201         src += (n-1)/BITS_PER_LONG;
202         if ((n-1) % BITS_PER_LONG) {
203                 dst_idx += (n-1) % BITS_PER_LONG;
204                 dst += dst_idx >> SHIFT_PER_LONG;
205                 dst_idx &= BITS_PER_LONG-1;
206                 src_idx += (n-1) % BITS_PER_LONG;
207                 src += src_idx >> SHIFT_PER_LONG;
208                 src_idx &= BITS_PER_LONG-1;
209         }
210         
211         shift = dst_idx-src_idx;
212         first = ~0UL << (BITS_PER_LONG-1-dst_idx);
213         last = ~(~0UL << (BITS_PER_LONG-1-((dst_idx-n) % BITS_PER_LONG)));
214         
215         if (!shift) {
216                 // Same alignment for source and dest
217                 
218                 if ((unsigned long)dst_idx+1 >= n) {
219                         // Single word
220                         if (last)
221                                 first &= last;
222                         FB_WRITEL((FB_READL(src) & first) | (FB_READL(dst) & ~first), dst); 
223                 } else {
224                         // Multiple destination words
225                         // Leading bits
226                         if (first) {
227                                 FB_WRITEL((FB_READL(src) & first) | (FB_READL(dst) & ~first), dst); 
228                                 dst--;
229                                 src--;
230                                 n -= dst_idx+1;
231                         }
232                         
233                         // Main chunk
234                         n /= BITS_PER_LONG;
235                         while (n >= 8) {
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                                 FB_WRITEL(FB_READL(src--), dst--);
244                                 n -= 8;
245                         }
246                         while (n--)
247                                 FB_WRITEL(FB_READL(src--), dst--);
248                         
249                         // Trailing bits
250                         if (last)
251                                 FB_WRITEL((FB_READL(src) & last) | (FB_READL(dst) & ~last), dst);
252                 }
253         } else {
254                 // Different alignment for source and dest
255                 
256                 right = shift & (BITS_PER_LONG-1);
257                 left = -shift & (BITS_PER_LONG-1);
258                 
259                 if ((unsigned long)dst_idx+1 >= n) {
260                         // Single destination word
261                         if (last)
262                                 first &= last;
263                         if (shift < 0) {
264                                 // Single source word
265                                 FB_WRITEL((FB_READL(src) << left & first) | 
266                                           (FB_READL(dst) & ~first), dst);
267                         } else if (1+(unsigned long)src_idx >= n) {
268                                 // Single source word
269                                 FB_WRITEL(((FB_READL(src) >> right) & first) | 
270                                           (FB_READL(dst) & ~first), dst);
271                         } else {
272                                 // 2 source words
273                                 d0 = FB_READL(src--);
274                                 d1 = FB_READL(src);
275                                 FB_WRITEL(((d0>>right | d1<<left) & first) | 
276                                           (FB_READL(dst) & ~first), dst);
277                         }
278                 } else {
279                         // Multiple destination words
280                         d0 = FB_READL(src--);
281                         // Leading bits
282                         if (shift < 0) {
283                                 // Single source word
284                                 FB_WRITEL(((d0 << left) & first) | 
285                                           (FB_READL(dst) & ~first), dst);
286                                 dst--;
287                                 n -= dst_idx+1;
288                         } else {
289                                 // 2 source words
290                                 d1 = FB_READL(src--);
291                                 FB_WRITEL(((d0>>right | d1<<left) & first) | 
292                                           (FB_READL(dst) & ~first), dst);
293                                 d0 = d1;
294                                 dst--;
295                                 n -= dst_idx+1;
296                         }
297                         
298                         // Main chunk
299                         m = n % BITS_PER_LONG;
300                         n /= BITS_PER_LONG;
301                         while (n >= 4) {
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                                 d1 = FB_READL(src--);
309                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
310                                 d0 = d1;
311                                 d1 = FB_READL(src--);
312                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
313                                 d0 = d1;
314                                 n -= 4;
315                         }
316                         while (n--) {
317                                 d1 = FB_READL(src--);
318                                 FB_WRITEL(d0 >> right | d1 << left, dst--);
319                                 d0 = d1;
320                         }
321                         
322                         // Trailing bits
323                         if (last) {
324                                 if (m <= left) {
325                                         // Single source word
326                                         FB_WRITEL(((d0 >> right) & last) | 
327                                                   (FB_READL(dst) & ~last), 
328                                                   dst);
329                                 } else {
330                                         // 2 source words
331                                         d1 = FB_READL(src);
332                                         FB_WRITEL(((d0>>right | d1<<left) & 
333                                                    last) | (FB_READL(dst) & 
334                                                             ~last), dst);
335                                 }
336                         }
337                 }
338         }
339 }
340
341 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
342 {
343         u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
344         u32 height = area->height, width = area->width;
345         int x2, y2, old_dx, old_dy, vxres, vyres;
346         unsigned long next_line = p->fix.line_length;
347         int dst_idx = 0, src_idx = 0, rev_copy = 0;
348         unsigned long __iomem *dst = NULL, *src = NULL;
349
350         if (p->state != FBINFO_STATE_RUNNING)
351                 return;
352
353         /* We want rotation but lack hardware to do it for us. */
354         if (!p->fbops->fb_rotate && p->var.rotate) {
355         }       
356         
357         vxres = p->var.xres_virtual;
358         vyres = p->var.yres_virtual;
359
360         if (area->dx > vxres || area->sx > vxres || 
361             area->dy > vyres || area->sy > vyres)
362                 return;
363
364         /* clip the destination */
365         old_dx = area->dx;
366         old_dy = area->dy;
367
368         /*
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         /* update sx1,sy1 */
382         sx += (dx - old_dx);
383         sy += (dy - old_dy);
384
385         /* the source must be completely inside the virtual screen */
386         if (sx < 0 || sy < 0 ||
387             (sx + width) > vxres ||
388             (sy + height) > vyres)
389                 return;
390
391         if ((dy == sy && dx > sx) ||    
392             (dy > sy)) { 
393                 dy += height;
394                 sy += height;
395                 rev_copy = 1;
396         }
397
398         dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & 
399                                       ~(BYTES_PER_LONG-1));
400         dst_idx = src_idx = (unsigned long)p->screen_base & (BYTES_PER_LONG-1);
401         dst_idx += dy*next_line*8 + dx*p->var.bits_per_pixel;
402         src_idx += sy*next_line*8 + sx*p->var.bits_per_pixel;
403         
404         if (p->fbops->fb_sync)
405                 p->fbops->fb_sync(p);
406         if (rev_copy) {
407                 while (height--) {
408                         dst_idx -= next_line*8;
409                         src_idx -= next_line*8;
410                         dst += dst_idx >> SHIFT_PER_LONG;
411                         dst_idx &= (BYTES_PER_LONG-1);
412                         src += src_idx >> SHIFT_PER_LONG;
413                         src_idx &= (BYTES_PER_LONG-1);
414                         bitcpy_rev(dst, dst_idx, src, src_idx, 
415                                    width*p->var.bits_per_pixel);
416                 }       
417         } else {
418                 while (height--) {
419                         dst += dst_idx >> SHIFT_PER_LONG;
420                         dst_idx &= (BYTES_PER_LONG-1);
421                         src += src_idx >> SHIFT_PER_LONG;
422                         src_idx &= (BYTES_PER_LONG-1);
423                         bitcpy(dst, dst_idx, src, src_idx, 
424                                width*p->var.bits_per_pixel);
425                         dst_idx += next_line*8;
426                         src_idx += next_line*8;
427                 }       
428         }
429 }
430
431 EXPORT_SYMBOL(cfb_copyarea);
432
433 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
434 MODULE_DESCRIPTION("Generic software accelerated copyarea");
435 MODULE_LICENSE("GPL");
436