Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6] / 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/module.h>
21 #include <linux/string.h>
22 #include <linux/fb.h>
23 #include <asm/types.h>
24
25 #if BITS_PER_LONG == 32
26 #  define FB_WRITEL fb_writel
27 #  define FB_READL  fb_readl
28 #else
29 #  define FB_WRITEL fb_writeq
30 #  define FB_READL  fb_readq
31 #endif
32
33     /*
34      *  Compose two values, using a bitmask as decision value
35      *  This is equivalent to (a & mask) | (b & ~mask)
36      */
37
38 static inline unsigned long
39 comp(unsigned long a, unsigned long b, unsigned long mask)
40 {
41     return ((a ^ b) & mask) ^ b;
42 }
43
44     /*
45      *  Create a pattern with the given pixel's color
46      */
47
48 #if BITS_PER_LONG == 64
49 static inline unsigned long
50 pixel_to_pat( u32 bpp, u32 pixel)
51 {
52         switch (bpp) {
53         case 1:
54                 return 0xfffffffffffffffful*pixel;
55         case 2:
56                 return 0x5555555555555555ul*pixel;
57         case 4:
58                 return 0x1111111111111111ul*pixel;
59         case 8:
60                 return 0x0101010101010101ul*pixel;
61         case 12:
62                 return 0x0001001001001001ul*pixel;
63         case 16:
64                 return 0x0001000100010001ul*pixel;
65         case 24:
66                 return 0x0000000001000001ul*pixel;
67         case 32:
68                 return 0x0000000100000001ul*pixel;
69         default:
70                 panic("pixel_to_pat(): unsupported pixelformat\n");
71     }
72 }
73 #else
74 static inline unsigned long
75 pixel_to_pat( u32 bpp, u32 pixel)
76 {
77         switch (bpp) {
78         case 1:
79                 return 0xfffffffful*pixel;
80         case 2:
81                 return 0x55555555ul*pixel;
82         case 4:
83                 return 0x11111111ul*pixel;
84         case 8:
85                 return 0x01010101ul*pixel;
86         case 12:
87                 return 0x00001001ul*pixel;
88         case 16:
89                 return 0x00010001ul*pixel;
90         case 24:
91                 return 0x00000001ul*pixel;
92         case 32:
93                 return 0x00000001ul*pixel;
94         default:
95                 panic("pixel_to_pat(): unsupported pixelformat\n");
96     }
97 }
98 #endif
99
100     /*
101      *  Aligned pattern fill using 32/64-bit memory accesses
102      */
103
104 static void
105 bitfill_aligned(unsigned long __iomem *dst, int dst_idx, unsigned long pat, unsigned n, int bits)
106 {
107         unsigned long first, last;
108
109         if (!n)
110                 return;
111
112         first = FB_SHIFT_HIGH(~0UL, dst_idx);
113         last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
114
115         if (dst_idx+n <= bits) {
116                 // Single word
117                 if (last)
118                         first &= last;
119                 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
120         } else {
121                 // Multiple destination words
122
123                 // Leading bits
124                 if (first!= ~0UL) {
125                         FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
126                         dst++;
127                         n -= bits - dst_idx;
128                 }
129
130                 // Main chunk
131                 n /= bits;
132                 while (n >= 8) {
133                         FB_WRITEL(pat, dst++);
134                         FB_WRITEL(pat, dst++);
135                         FB_WRITEL(pat, dst++);
136                         FB_WRITEL(pat, dst++);
137                         FB_WRITEL(pat, dst++);
138                         FB_WRITEL(pat, dst++);
139                         FB_WRITEL(pat, dst++);
140                         FB_WRITEL(pat, dst++);
141                         n -= 8;
142                 }
143                 while (n--)
144                         FB_WRITEL(pat, dst++);
145
146                 // Trailing bits
147                 if (last)
148                         FB_WRITEL(comp(pat, FB_READL(dst), last), dst);
149         }
150 }
151
152
153     /*
154      *  Unaligned generic pattern fill using 32/64-bit memory accesses
155      *  The pattern must have been expanded to a full 32/64-bit value
156      *  Left/right are the appropriate shifts to convert to the pattern to be
157      *  used for the next 32/64-bit word
158      */
159
160 static void
161 bitfill_unaligned(unsigned long __iomem *dst, int dst_idx, unsigned long pat,
162                         int left, int right, unsigned n, int bits)
163 {
164         unsigned long first, last;
165
166         if (!n)
167                 return;
168
169         first = FB_SHIFT_HIGH(~0UL, dst_idx);
170         last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
171
172         if (dst_idx+n <= bits) {
173                 // Single word
174                 if (last)
175                         first &= last;
176                 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
177         } else {
178                 // Multiple destination words
179                 // Leading bits
180                 if (first) {
181                         FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
182                         dst++;
183                         pat = pat << left | pat >> right;
184                         n -= bits - dst_idx;
185                 }
186
187                 // Main chunk
188                 n /= bits;
189                 while (n >= 4) {
190                         FB_WRITEL(pat, dst++);
191                         pat = pat << left | pat >> right;
192                         FB_WRITEL(pat, dst++);
193                         pat = pat << left | pat >> right;
194                         FB_WRITEL(pat, dst++);
195                         pat = pat << left | pat >> right;
196                         FB_WRITEL(pat, dst++);
197                         pat = pat << left | pat >> right;
198                         n -= 4;
199                 }
200                 while (n--) {
201                         FB_WRITEL(pat, dst++);
202                         pat = pat << left | pat >> right;
203                 }
204
205                 // Trailing bits
206                 if (last)
207                         FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
208         }
209 }
210
211     /*
212      *  Aligned pattern invert using 32/64-bit memory accesses
213      */
214 static void
215 bitfill_aligned_rev(unsigned long __iomem *dst, int dst_idx, unsigned long pat, unsigned n, int bits)
216 {
217         unsigned long val = pat, dat;
218         unsigned long first, last;
219
220         if (!n)
221                 return;
222
223         first = FB_SHIFT_HIGH(~0UL, dst_idx);
224         last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
225
226         if (dst_idx+n <= bits) {
227                 // Single word
228                 if (last)
229                         first &= last;
230                 dat = FB_READL(dst);
231                 FB_WRITEL(comp(dat ^ val, dat, first), dst);
232         } else {
233                 // Multiple destination words
234                 // Leading bits
235                 if (first!=0UL) {
236                         dat = FB_READL(dst);
237                         FB_WRITEL(comp(dat ^ val, dat, first), dst);
238                         dst++;
239                         n -= bits - dst_idx;
240                 }
241
242                 // Main chunk
243                 n /= bits;
244                 while (n >= 8) {
245                         FB_WRITEL(FB_READL(dst) ^ val, dst);
246                         dst++;
247                         FB_WRITEL(FB_READL(dst) ^ val, dst);
248                         dst++;
249                         FB_WRITEL(FB_READL(dst) ^ val, dst);
250                         dst++;
251                         FB_WRITEL(FB_READL(dst) ^ val, dst);
252                         dst++;
253                         FB_WRITEL(FB_READL(dst) ^ val, dst);
254                         dst++;
255                         FB_WRITEL(FB_READL(dst) ^ val, dst);
256                         dst++;
257                         FB_WRITEL(FB_READL(dst) ^ val, dst);
258                         dst++;
259                         FB_WRITEL(FB_READL(dst) ^ val, dst);
260                         dst++;
261                         n -= 8;
262                 }
263                 while (n--) {
264                         FB_WRITEL(FB_READL(dst) ^ val, dst);
265                         dst++;
266                 }
267                 // Trailing bits
268                 if (last) {
269                         dat = FB_READL(dst);
270                         FB_WRITEL(comp(dat ^ val, dat, last), dst);
271                 }
272         }
273 }
274
275
276     /*
277      *  Unaligned generic pattern invert using 32/64-bit memory accesses
278      *  The pattern must have been expanded to a full 32/64-bit value
279      *  Left/right are the appropriate shifts to convert to the pattern to be
280      *  used for the next 32/64-bit word
281      */
282
283 static void
284 bitfill_unaligned_rev(unsigned long __iomem *dst, int dst_idx, unsigned long pat,
285                         int left, int right, unsigned n, int bits)
286 {
287         unsigned long first, last, dat;
288
289         if (!n)
290                 return;
291
292         first = FB_SHIFT_HIGH(~0UL, dst_idx);
293         last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
294
295         if (dst_idx+n <= bits) {
296                 // Single word
297                 if (last)
298                         first &= last;
299                 dat = FB_READL(dst);
300                 FB_WRITEL(comp(dat ^ pat, dat, first), dst);
301         } else {
302                 // Multiple destination words
303
304                 // Leading bits
305                 if (first != 0UL) {
306                         dat = FB_READL(dst);
307                         FB_WRITEL(comp(dat ^ pat, dat, first), dst);
308                         dst++;
309                         pat = pat << left | pat >> right;
310                         n -= bits - dst_idx;
311                 }
312
313                 // Main chunk
314                 n /= bits;
315                 while (n >= 4) {
316                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
317                         dst++;
318                         pat = pat << left | pat >> right;
319                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
320                         dst++;
321                         pat = pat << left | pat >> right;
322                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
323                         dst++;
324                         pat = pat << left | pat >> right;
325                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
326                         dst++;
327                         pat = pat << left | pat >> right;
328                         n -= 4;
329                 }
330                 while (n--) {
331                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
332                         dst++;
333                         pat = pat << left | pat >> right;
334                 }
335
336                 // Trailing bits
337                 if (last) {
338                         dat = FB_READL(dst);
339                         FB_WRITEL(comp(dat ^ pat, dat, last), dst);
340                 }
341         }
342 }
343
344 void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
345 {
346         unsigned long pat, fg;
347         unsigned long width = rect->width, height = rect->height;
348         int bits = BITS_PER_LONG, bytes = bits >> 3;
349         u32 bpp = p->var.bits_per_pixel;
350         unsigned long __iomem *dst;
351         int dst_idx, left;
352
353         if (p->state != FBINFO_STATE_RUNNING)
354                 return;
355
356         if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
357             p->fix.visual == FB_VISUAL_DIRECTCOLOR )
358                 fg = ((u32 *) (p->pseudo_palette))[rect->color];
359         else
360                 fg = rect->color;
361
362         pat = pixel_to_pat( bpp, fg);
363
364         dst = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
365         dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
366         dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
367         /* FIXME For now we support 1-32 bpp only */
368         left = bits % bpp;
369         if (p->fbops->fb_sync)
370                 p->fbops->fb_sync(p);
371         if (!left) {
372                 void (*fill_op32)(unsigned long __iomem *dst, int dst_idx,
373                                   unsigned long pat, unsigned n, int bits) = NULL;
374
375                 switch (rect->rop) {
376                 case ROP_XOR:
377                         fill_op32 = bitfill_aligned_rev;
378                         break;
379                 case ROP_COPY:
380                         fill_op32 = bitfill_aligned;
381                         break;
382                 default:
383                         printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
384                         fill_op32 = bitfill_aligned;
385                         break;
386                 }
387                 while (height--) {
388                         dst += dst_idx >> (ffs(bits) - 1);
389                         dst_idx &= (bits - 1);
390                         fill_op32(dst, dst_idx, pat, width*bpp, bits);
391                         dst_idx += p->fix.line_length*8;
392                 }
393         } else {
394                 int right;
395                 int r;
396                 int rot = (left-dst_idx) % bpp;
397                 void (*fill_op)(unsigned long __iomem *dst, int dst_idx,
398                                 unsigned long pat, int left, int right,
399                                 unsigned n, int bits) = NULL;
400
401                 /* rotate pattern to correct start position */
402                 pat = pat << rot | pat >> (bpp-rot);
403
404                 right = bpp-left;
405                 switch (rect->rop) {
406                 case ROP_XOR:
407                         fill_op = bitfill_unaligned_rev;
408                         break;
409                 case ROP_COPY:
410                         fill_op = bitfill_unaligned;
411                         break;
412                 default:
413                         printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
414                         fill_op = bitfill_unaligned;
415                         break;
416                 }
417                 while (height--) {
418                         dst += dst_idx >> (ffs(bits) - 1);
419                         dst_idx &= (bits - 1);
420                         fill_op(dst, dst_idx, pat, left, right,
421                                 width*bpp, bits);
422                         r = (p->fix.line_length*8) % bpp;
423                         pat = pat << (bpp-r) | pat >> r;
424                         dst_idx += p->fix.line_length*8;
425                 }
426         }
427 }
428
429 EXPORT_SYMBOL(cfb_fillrect);
430
431 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
432 MODULE_DESCRIPTION("Generic software accelerated fill rectangle");
433 MODULE_LICENSE("GPL");