ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / video / cfbfillrect.c
1 /*
2  *  Generic fillrect for frame buffers with packed pixels of any depth. 
3  *
4  *      Copyright (C)  2000 James Simmons (jsimmons@linux-fbdev.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  *  The code for depths like 24 that don't have integer number of pixels per 
13  *  long is broken and needs to be fixed. For now I turned these types of 
14  *  mode off.
15  *
16  *  Also need to add code to deal with cards endians that are different than
17  *  the native cpu endians. I also need to deal with MSB position in the word.
18  *
19  */
20 #include <linux/config.h>
21 #include <linux/module.h>
22 #include <linux/string.h>
23 #include <linux/fb.h>
24 #include <asm/types.h>
25
26 #if BITS_PER_LONG == 32
27 #define FB_WRITEL fb_writel
28 #define FB_READL  fb_readl
29 #define BYTES_PER_LONG 4
30 #define SHIFT_PER_LONG 5
31 #else
32 #define FB_WRITEL fb_writeq
33 #define FB_READL  fb_readq
34 #define BYTES_PER_LONG 8
35 #define SHIFT_PER_LONG 6
36 #endif
37
38 #define EXP1(x)         0xffffffffU*x
39 #define EXP2(x)         0x55555555U*x
40 #define EXP4(x)         0x11111111U*0x ## x
41
42 typedef u32 pixel_t;
43
44 static const u32 bpp1tab[2] = {
45     EXP1(0), EXP1(1)
46 };
47
48 static const u32 bpp2tab[4] = {
49     EXP2(0), EXP2(1), EXP2(2), EXP2(3)
50 };
51
52 static const u32 bpp4tab[16] = {
53     EXP4(0), EXP4(1), EXP4(2), EXP4(3), EXP4(4), EXP4(5), EXP4(6), EXP4(7),
54     EXP4(8), EXP4(9), EXP4(a), EXP4(b), EXP4(c), EXP4(d), EXP4(e), EXP4(f)
55 };
56
57     /*
58      *  Compose two values, using a bitmask as decision value
59      *  This is equivalent to (a & mask) | (b & ~mask)
60      */
61
62 static inline unsigned long comp(unsigned long a, unsigned long b,
63                                  unsigned long mask)
64 {
65     return ((a ^ b) & mask) ^ b;
66 }
67
68 static inline u32 pixel_to_pat32(const struct fb_info *p, pixel_t pixel)
69 {
70     u32 pat = pixel;
71
72     switch (p->var.bits_per_pixel) {
73         case 1:
74             pat = bpp1tab[pat];
75             break;
76
77         case 2:
78             pat = bpp2tab[pat];
79             break;
80
81         case 4:
82             pat = bpp4tab[pat];
83             break;
84
85         case 8:
86             pat |= pat << 8;
87             // Fall through
88         case 16:
89             pat |= pat << 16;
90             // Fall through
91         case 32:
92             break;
93     }
94     return pat;
95 }
96
97     /*
98      *  Expand a pixel value to a generic 32/64-bit pattern and rotate it to
99      *  the correct start position
100      */
101
102 static inline unsigned long pixel_to_pat(const struct fb_info *p, 
103                                          pixel_t pixel, int left)
104 {
105     unsigned long pat = pixel;
106     u32 bpp = p->var.bits_per_pixel;
107     int i;
108
109     /* expand pixel value */
110     for (i = bpp; i < BITS_PER_LONG; i *= 2)
111         pat |= pat << i;
112
113     /* rotate pattern to correct start position */
114     pat = pat << left | pat >> (bpp-left);
115     return pat;
116 }
117
118     /*
119      *  Unaligned 32-bit pattern fill using 32/64-bit memory accesses
120      */
121
122 void bitfill32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
123 {
124         unsigned long val = pat;
125         unsigned long first, last;
126         
127         if (!n)
128                 return;
129         
130 #if BITS_PER_LONG == 64
131         val |= val << 32;
132 #endif
133         
134         first = ~0UL >> dst_idx;
135         last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
136         
137         if (dst_idx+n <= BITS_PER_LONG) {
138                 // Single word
139                 if (last)
140                         first &= last;
141                 FB_WRITEL(comp(val, FB_READL(dst), first), dst);
142         } else {
143                 // Multiple destination words
144                 // Leading bits
145                 if (first) {
146                         FB_WRITEL(comp(val, FB_READL(dst), first), dst);
147                         dst++;
148                         n -= BITS_PER_LONG-dst_idx;
149                 }
150                 
151                 // Main chunk
152                 n /= BITS_PER_LONG;
153                 while (n >= 8) {
154                         FB_WRITEL(val, dst++);
155                         FB_WRITEL(val, dst++);
156                         FB_WRITEL(val, dst++);
157                         FB_WRITEL(val, dst++);
158                         FB_WRITEL(val, dst++);
159                         FB_WRITEL(val, dst++);
160                         FB_WRITEL(val, dst++);
161                         FB_WRITEL(val, dst++);
162                         n -= 8;
163                 }
164                 while (n--)
165                         FB_WRITEL(val, dst++);
166                 
167                 // Trailing bits
168                 if (last)
169                         FB_WRITEL(comp(val, FB_READL(dst), first), dst);
170         }
171 }
172
173
174     /*
175      *  Unaligned generic pattern fill using 32/64-bit memory accesses
176      *  The pattern must have been expanded to a full 32/64-bit value
177      *  Left/right are the appropriate shifts to convert to the pattern to be
178      *  used for the next 32/64-bit word
179      */
180
181 void bitfill(unsigned long *dst, int dst_idx, unsigned long pat, int left,
182              int right, u32 n)
183 {
184         unsigned long first, last;
185
186         if (!n)
187                 return;
188         
189         first = ~0UL >> dst_idx;
190         last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
191         
192         if (dst_idx+n <= BITS_PER_LONG) {
193                 // Single word
194                 if (last)
195                         first &= last;
196                 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
197         } else {
198                 // Multiple destination words
199                 // Leading bits
200                 if (first) {
201                         FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
202                         dst++;
203                         pat = pat << left | pat >> right;
204                         n -= BITS_PER_LONG-dst_idx;
205                 }
206                 
207                 // Main chunk
208                 n /= BITS_PER_LONG;
209                 while (n >= 4) {
210                         FB_WRITEL(pat, dst++);
211                         pat = pat << left | pat >> right;
212                         FB_WRITEL(pat, dst++);
213                         pat = pat << left | pat >> right;
214                         FB_WRITEL(pat, dst++);
215                         pat = pat << left | pat >> right;
216                         FB_WRITEL(pat, dst++);
217                         pat = pat << left | pat >> right;
218                         n -= 4;
219                 }
220                 while (n--) {
221                         FB_WRITEL(pat, dst++);
222                         pat = pat << left | pat >> right;
223                 }
224                 
225                 // Trailing bits
226                 if (last)
227                         FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
228         }
229 }
230
231 void bitfill32_rev(unsigned long *dst, int dst_idx, u32 pat, u32 n)
232 {
233         unsigned long val = pat, dat;
234         unsigned long first, last;
235         
236         if (!n)
237                 return;
238         
239 #if BITS_PER_LONG == 64
240         val |= val << 32;
241 #endif
242         
243         first = ~0UL >> dst_idx;
244         last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
245         
246         if (dst_idx+n <= BITS_PER_LONG) {
247                 // Single word
248                 if (last)
249                         first &= last;
250                 dat = FB_READL(dst);
251                 FB_WRITEL(comp(dat ^ val, dat, first), dst);
252         } else {
253                 // Multiple destination words
254                 // Leading bits
255                 if (first) {
256                         dat = FB_READL(dst);
257                         FB_WRITEL(comp(dat ^ val, dat, first), dst);
258                         dst++;
259                         n -= BITS_PER_LONG-dst_idx;
260                 }
261                 
262                 // Main chunk
263                 n /= BITS_PER_LONG;
264                 while (n >= 8) {
265                         FB_WRITEL(FB_READL(dst) ^ val, dst);
266                         dst++;
267                         FB_WRITEL(FB_READL(dst) ^ val, dst);
268                         dst++;
269                         FB_WRITEL(FB_READL(dst) ^ val, dst);
270                         dst++;
271                         FB_WRITEL(FB_READL(dst) ^ val, dst);
272                         dst++;
273                         FB_WRITEL(FB_READL(dst) ^ val, dst);
274                         dst++;
275                         FB_WRITEL(FB_READL(dst) ^ val, dst);
276                         dst++;
277                         FB_WRITEL(FB_READL(dst) ^ val, dst);
278                         dst++;
279                         FB_WRITEL(FB_READL(dst) ^ val, dst);
280                         dst++;
281                         n -= 8;
282                 }
283                 while (n--) {
284                         FB_WRITEL(FB_READL(dst) ^ val, dst);
285                         dst++;
286                 }               
287                 // Trailing bits
288                 if (last) {
289                         dat = FB_READL(dst);
290                         FB_WRITEL(comp(dat ^ val, dat, first), dst);
291                 }
292         }
293 }
294
295
296     /*
297      *  Unaligned generic pattern fill using 32/64-bit memory accesses
298      *  The pattern must have been expanded to a full 32/64-bit value
299      *  Left/right are the appropriate shifts to convert to the pattern to be
300      *  used for the next 32/64-bit word
301      */
302
303 void bitfill_rev(unsigned long *dst, int dst_idx, unsigned long pat, int left,
304              int right, u32 n)
305 {
306         unsigned long first, last, dat;
307
308         if (!n)
309                 return;
310         
311         first = ~0UL >> dst_idx;
312         last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
313         
314         if (dst_idx+n <= BITS_PER_LONG) {
315                 // Single word
316                 if (last)
317                         first &= last;
318                 dat = FB_READL(dst);
319                 FB_WRITEL(comp(dat ^ pat, dat, first), dst);
320         } else {
321                 // Multiple destination words
322                 // Leading bits
323                 if (first) {
324                         dat = FB_READL(dst);
325                         FB_WRITEL(comp(dat ^ pat, dat, first), dst);
326                         dst++;
327                         pat = pat << left | pat >> right;
328                         n -= BITS_PER_LONG-dst_idx;
329                 }
330                 
331                 // Main chunk
332                 n /= BITS_PER_LONG;
333                 while (n >= 4) {
334                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
335                         dst++;
336                         pat = pat << left | pat >> right;
337                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
338                         dst++;
339                         pat = pat << left | pat >> right;
340                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
341                         dst++;
342                         pat = pat << left | pat >> right;
343                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
344                         dst++;
345                         pat = pat << left | pat >> right;
346                         n -= 4;
347                 }
348                 while (n--) {
349                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
350                         dst++;
351                         pat = pat << left | pat >> right;
352                 }
353                 
354                 // Trailing bits
355                 if (last) {
356                         dat = FB_READL(dst);
357                         FB_WRITEL(comp(dat ^ pat, dat, first), dst);
358                 }
359         }
360 }
361
362 void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
363 {
364         u32 bpp = p->var.bits_per_pixel;
365         unsigned long x2, y2, vxres, vyres;
366         unsigned long height, width, fg;
367         unsigned long *dst;
368         int dst_idx, left;
369
370         if (p->state != FBINFO_STATE_RUNNING)
371                 return;
372
373         /* We want rotation but lack hardware to do it for us. */
374         if (!p->fbops->fb_rotate && p->var.rotate) {
375         }       
376         
377         vxres = p->var.xres_virtual;
378         vyres = p->var.yres_virtual;
379
380         if (!rect->width || !rect->height || 
381             rect->dx > vxres || rect->dy > vyres)
382                 return;
383
384         /* We could use hardware clipping but on many cards you get around
385          * hardware clipping by writing to framebuffer directly. */
386         
387         x2 = rect->dx + rect->width;
388         y2 = rect->dy + rect->height;
389         x2 = x2 < vxres ? x2 : vxres;
390         y2 = y2 < vyres ? y2 : vyres;
391         width = x2 - rect->dx;
392         height = y2 - rect->dy;
393         
394         if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
395             p->fix.visual == FB_VISUAL_DIRECTCOLOR )
396                 fg = ((u32 *) (p->pseudo_palette))[rect->color];
397         else
398                 fg = rect->color;
399         
400         dst = (unsigned long *)((unsigned long)p->screen_base & 
401                                 ~(BYTES_PER_LONG-1));
402         dst_idx = ((unsigned long)p->screen_base & (BYTES_PER_LONG-1))*8;
403         dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
404         /* FIXME For now we support 1-32 bpp only */
405         left = BITS_PER_LONG % bpp;
406         if (p->fbops->fb_sync)
407                 p->fbops->fb_sync(p);
408         if (!left) {
409                 u32 pat = pixel_to_pat32(p, fg);
410                 void (*fill_op32)(unsigned long *dst, int dst_idx, u32 pat, 
411                                   u32 n) = NULL;
412                 
413                 switch (rect->rop) {
414                 case ROP_XOR:
415                         fill_op32 = bitfill32_rev;
416                         break;
417                 case ROP_COPY:
418                 default:
419                         fill_op32 = bitfill32;
420                         break;
421                 }
422                 while (height--) {
423                         dst += dst_idx >> SHIFT_PER_LONG;
424                         dst_idx &= (BITS_PER_LONG-1);
425                         fill_op32(dst, dst_idx, pat, width*bpp);
426                         dst_idx += p->fix.line_length*8;
427                 }
428         } else {
429                 unsigned long pat = pixel_to_pat(p, fg, (left-dst_idx) % bpp);
430                 int right = bpp-left;
431                 int r;
432                 void (*fill_op)(unsigned long *dst, int dst_idx, 
433                                 unsigned long pat, int left, int right, 
434                                 u32 n) = NULL;
435                 
436                 switch (rect->rop) {
437                 case ROP_XOR:
438                         fill_op = bitfill_rev;
439                         break;
440                 case ROP_COPY:
441                 default:
442                         fill_op = bitfill;
443                         break;
444                 }
445                 while (height--) {
446                         dst += dst_idx >> SHIFT_PER_LONG;
447                         dst_idx &= (BITS_PER_LONG-1);
448                         fill_op(dst, dst_idx, pat, left, right, 
449                                 width*bpp);
450                         r = (p->fix.line_length*8) % bpp;
451                         pat = pat << (bpp-r) | pat >> r;
452                         dst_idx += p->fix.line_length*8;
453                 }
454         }
455 }
456
457 EXPORT_SYMBOL(cfb_fillrect);
458
459 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
460 MODULE_DESCRIPTION("Generic software accelerated fill rectangle");
461 MODULE_LICENSE("GPL");