fbdev: consolidate common drawing functions into a header file
[safe/jmp/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 #include "fb_draw.h"
25
26 #if BITS_PER_LONG == 32
27 #  define FB_WRITEL fb_writel
28 #  define FB_READL  fb_readl
29 #else
30 #  define FB_WRITEL fb_writeq
31 #  define FB_READL  fb_readq
32 #endif
33
34     /*
35      *  Aligned pattern fill using 32/64-bit memory accesses
36      */
37
38 static void
39 bitfill_aligned(unsigned long __iomem *dst, int dst_idx, unsigned long pat, unsigned n, int bits)
40 {
41         unsigned long first, last;
42
43         if (!n)
44                 return;
45
46         first = FB_SHIFT_HIGH(~0UL, dst_idx);
47         last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
48
49         if (dst_idx+n <= bits) {
50                 // Single word
51                 if (last)
52                         first &= last;
53                 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
54         } else {
55                 // Multiple destination words
56
57                 // Leading bits
58                 if (first!= ~0UL) {
59                         FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
60                         dst++;
61                         n -= bits - dst_idx;
62                 }
63
64                 // Main chunk
65                 n /= bits;
66                 while (n >= 8) {
67                         FB_WRITEL(pat, dst++);
68                         FB_WRITEL(pat, dst++);
69                         FB_WRITEL(pat, dst++);
70                         FB_WRITEL(pat, dst++);
71                         FB_WRITEL(pat, dst++);
72                         FB_WRITEL(pat, dst++);
73                         FB_WRITEL(pat, dst++);
74                         FB_WRITEL(pat, dst++);
75                         n -= 8;
76                 }
77                 while (n--)
78                         FB_WRITEL(pat, dst++);
79
80                 // Trailing bits
81                 if (last)
82                         FB_WRITEL(comp(pat, FB_READL(dst), last), dst);
83         }
84 }
85
86
87     /*
88      *  Unaligned generic pattern fill using 32/64-bit memory accesses
89      *  The pattern must have been expanded to a full 32/64-bit value
90      *  Left/right are the appropriate shifts to convert to the pattern to be
91      *  used for the next 32/64-bit word
92      */
93
94 static void
95 bitfill_unaligned(unsigned long __iomem *dst, int dst_idx, unsigned long pat,
96                         int left, int right, unsigned n, int bits)
97 {
98         unsigned long first, last;
99
100         if (!n)
101                 return;
102
103         first = FB_SHIFT_HIGH(~0UL, dst_idx);
104         last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
105
106         if (dst_idx+n <= bits) {
107                 // Single word
108                 if (last)
109                         first &= last;
110                 FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
111         } else {
112                 // Multiple destination words
113                 // Leading bits
114                 if (first) {
115                         FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
116                         dst++;
117                         pat = pat << left | pat >> right;
118                         n -= bits - dst_idx;
119                 }
120
121                 // Main chunk
122                 n /= bits;
123                 while (n >= 4) {
124                         FB_WRITEL(pat, dst++);
125                         pat = pat << left | pat >> right;
126                         FB_WRITEL(pat, dst++);
127                         pat = pat << left | pat >> right;
128                         FB_WRITEL(pat, dst++);
129                         pat = pat << left | pat >> right;
130                         FB_WRITEL(pat, dst++);
131                         pat = pat << left | pat >> right;
132                         n -= 4;
133                 }
134                 while (n--) {
135                         FB_WRITEL(pat, dst++);
136                         pat = pat << left | pat >> right;
137                 }
138
139                 // Trailing bits
140                 if (last)
141                         FB_WRITEL(comp(pat, FB_READL(dst), first), dst);
142         }
143 }
144
145     /*
146      *  Aligned pattern invert using 32/64-bit memory accesses
147      */
148 static void
149 bitfill_aligned_rev(unsigned long __iomem *dst, int dst_idx, unsigned long pat, unsigned n, int bits)
150 {
151         unsigned long val = pat, dat;
152         unsigned long first, last;
153
154         if (!n)
155                 return;
156
157         first = FB_SHIFT_HIGH(~0UL, dst_idx);
158         last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
159
160         if (dst_idx+n <= bits) {
161                 // Single word
162                 if (last)
163                         first &= last;
164                 dat = FB_READL(dst);
165                 FB_WRITEL(comp(dat ^ val, dat, first), dst);
166         } else {
167                 // Multiple destination words
168                 // Leading bits
169                 if (first!=0UL) {
170                         dat = FB_READL(dst);
171                         FB_WRITEL(comp(dat ^ val, dat, first), dst);
172                         dst++;
173                         n -= bits - dst_idx;
174                 }
175
176                 // Main chunk
177                 n /= bits;
178                 while (n >= 8) {
179                         FB_WRITEL(FB_READL(dst) ^ val, dst);
180                         dst++;
181                         FB_WRITEL(FB_READL(dst) ^ val, dst);
182                         dst++;
183                         FB_WRITEL(FB_READL(dst) ^ val, dst);
184                         dst++;
185                         FB_WRITEL(FB_READL(dst) ^ val, dst);
186                         dst++;
187                         FB_WRITEL(FB_READL(dst) ^ val, dst);
188                         dst++;
189                         FB_WRITEL(FB_READL(dst) ^ val, dst);
190                         dst++;
191                         FB_WRITEL(FB_READL(dst) ^ val, dst);
192                         dst++;
193                         FB_WRITEL(FB_READL(dst) ^ val, dst);
194                         dst++;
195                         n -= 8;
196                 }
197                 while (n--) {
198                         FB_WRITEL(FB_READL(dst) ^ val, dst);
199                         dst++;
200                 }
201                 // Trailing bits
202                 if (last) {
203                         dat = FB_READL(dst);
204                         FB_WRITEL(comp(dat ^ val, dat, last), dst);
205                 }
206         }
207 }
208
209
210     /*
211      *  Unaligned generic pattern invert using 32/64-bit memory accesses
212      *  The pattern must have been expanded to a full 32/64-bit value
213      *  Left/right are the appropriate shifts to convert to the pattern to be
214      *  used for the next 32/64-bit word
215      */
216
217 static void
218 bitfill_unaligned_rev(unsigned long __iomem *dst, int dst_idx, unsigned long pat,
219                         int left, int right, unsigned n, int bits)
220 {
221         unsigned long first, last, dat;
222
223         if (!n)
224                 return;
225
226         first = FB_SHIFT_HIGH(~0UL, dst_idx);
227         last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
228
229         if (dst_idx+n <= bits) {
230                 // Single word
231                 if (last)
232                         first &= last;
233                 dat = FB_READL(dst);
234                 FB_WRITEL(comp(dat ^ pat, dat, first), dst);
235         } else {
236                 // Multiple destination words
237
238                 // Leading bits
239                 if (first != 0UL) {
240                         dat = FB_READL(dst);
241                         FB_WRITEL(comp(dat ^ pat, dat, first), dst);
242                         dst++;
243                         pat = pat << left | pat >> right;
244                         n -= bits - dst_idx;
245                 }
246
247                 // Main chunk
248                 n /= bits;
249                 while (n >= 4) {
250                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
251                         dst++;
252                         pat = pat << left | pat >> right;
253                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
254                         dst++;
255                         pat = pat << left | pat >> right;
256                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
257                         dst++;
258                         pat = pat << left | pat >> right;
259                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
260                         dst++;
261                         pat = pat << left | pat >> right;
262                         n -= 4;
263                 }
264                 while (n--) {
265                         FB_WRITEL(FB_READL(dst) ^ pat, dst);
266                         dst++;
267                         pat = pat << left | pat >> right;
268                 }
269
270                 // Trailing bits
271                 if (last) {
272                         dat = FB_READL(dst);
273                         FB_WRITEL(comp(dat ^ pat, dat, last), dst);
274                 }
275         }
276 }
277
278 void cfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
279 {
280         unsigned long pat, fg;
281         unsigned long width = rect->width, height = rect->height;
282         int bits = BITS_PER_LONG, bytes = bits >> 3;
283         u32 bpp = p->var.bits_per_pixel;
284         unsigned long __iomem *dst;
285         int dst_idx, left;
286
287         if (p->state != FBINFO_STATE_RUNNING)
288                 return;
289
290         if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
291             p->fix.visual == FB_VISUAL_DIRECTCOLOR )
292                 fg = ((u32 *) (p->pseudo_palette))[rect->color];
293         else
294                 fg = rect->color;
295
296         pat = pixel_to_pat( bpp, fg);
297
298         dst = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
299         dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
300         dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
301         /* FIXME For now we support 1-32 bpp only */
302         left = bits % bpp;
303         if (p->fbops->fb_sync)
304                 p->fbops->fb_sync(p);
305         if (!left) {
306                 void (*fill_op32)(unsigned long __iomem *dst, int dst_idx,
307                                   unsigned long pat, unsigned n, int bits) = NULL;
308
309                 switch (rect->rop) {
310                 case ROP_XOR:
311                         fill_op32 = bitfill_aligned_rev;
312                         break;
313                 case ROP_COPY:
314                         fill_op32 = bitfill_aligned;
315                         break;
316                 default:
317                         printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
318                         fill_op32 = bitfill_aligned;
319                         break;
320                 }
321                 while (height--) {
322                         dst += dst_idx >> (ffs(bits) - 1);
323                         dst_idx &= (bits - 1);
324                         fill_op32(dst, dst_idx, pat, width*bpp, bits);
325                         dst_idx += p->fix.line_length*8;
326                 }
327         } else {
328                 int right;
329                 int r;
330                 int rot = (left-dst_idx) % bpp;
331                 void (*fill_op)(unsigned long __iomem *dst, int dst_idx,
332                                 unsigned long pat, int left, int right,
333                                 unsigned n, int bits) = NULL;
334
335                 /* rotate pattern to correct start position */
336                 pat = pat << rot | pat >> (bpp-rot);
337
338                 right = bpp-left;
339                 switch (rect->rop) {
340                 case ROP_XOR:
341                         fill_op = bitfill_unaligned_rev;
342                         break;
343                 case ROP_COPY:
344                         fill_op = bitfill_unaligned;
345                         break;
346                 default:
347                         printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
348                         fill_op = bitfill_unaligned;
349                         break;
350                 }
351                 while (height--) {
352                         dst += dst_idx >> (ffs(bits) - 1);
353                         dst_idx &= (bits - 1);
354                         fill_op(dst, dst_idx, pat, left, right,
355                                 width*bpp, bits);
356                         r = (p->fix.line_length*8) % bpp;
357                         pat = pat << (bpp-r) | pat >> r;
358                         dst_idx += p->fix.line_length*8;
359                 }
360         }
361 }
362
363 EXPORT_SYMBOL(cfb_fillrect);
364
365 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
366 MODULE_DESCRIPTION("Generic software accelerated fill rectangle");
367 MODULE_LICENSE("GPL");