drivers/video/uvesafb.c: fix error-path memory leak
[linux-2.6] / drivers / video / sysfillrect.c
1 /*
2  *  Generic fillrect for frame buffers in system RAM with packed pixels of
3  *  any depth.
4  *
5  *  Based almost entirely from cfbfillrect.c (which is based almost entirely
6  *  on Geert Uytterhoeven's fillrect routine)
7  *
8  *      Copyright (C)  2007 Antonino Daplas <adaplas@pol.net>
9  *
10  *  This file is subject to the terms and conditions of the GNU General Public
11  *  License.  See the file COPYING in the main directory of this archive for
12  *  more details.
13  */
14 #include <linux/module.h>
15 #include <linux/string.h>
16 #include <linux/fb.h>
17 #include <asm/types.h>
18 #include "fb_draw.h"
19
20     /*
21      *  Aligned pattern fill using 32/64-bit memory accesses
22      */
23
24 static void
25 bitfill_aligned(struct fb_info *p, unsigned long *dst, int dst_idx,
26                 unsigned long pat, unsigned n, int bits)
27 {
28         unsigned long first, last;
29
30         if (!n)
31                 return;
32
33         first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
34         last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
35
36         if (dst_idx+n <= bits) {
37                 /* Single word */
38                 if (last)
39                         first &= last;
40                 *dst = comp(pat, *dst, first);
41         } else {
42                 /* Multiple destination words */
43
44                 /* Leading bits */
45                 if (first!= ~0UL) {
46                         *dst = comp(pat, *dst, first);
47                         dst++;
48                         n -= bits - dst_idx;
49                 }
50
51                 /* Main chunk */
52                 n /= bits;
53                 while (n >= 8) {
54                         *dst++ = pat;
55                         *dst++ = pat;
56                         *dst++ = pat;
57                         *dst++ = pat;
58                         *dst++ = pat;
59                         *dst++ = pat;
60                         *dst++ = pat;
61                         *dst++ = pat;
62                         n -= 8;
63                 }
64                 while (n--)
65                         *dst++ = pat;
66                 /* Trailing bits */
67                 if (last)
68                         *dst = comp(pat, *dst, last);
69         }
70 }
71
72
73     /*
74      *  Unaligned generic pattern fill using 32/64-bit memory accesses
75      *  The pattern must have been expanded to a full 32/64-bit value
76      *  Left/right are the appropriate shifts to convert to the pattern to be
77      *  used for the next 32/64-bit word
78      */
79
80 static void
81 bitfill_unaligned(struct fb_info *p, unsigned long *dst, int dst_idx,
82                   unsigned long pat, int left, int right, unsigned n, int bits)
83 {
84         unsigned long first, last;
85
86         if (!n)
87                 return;
88
89         first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
90         last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
91
92         if (dst_idx+n <= bits) {
93                 /* Single word */
94                 if (last)
95                         first &= last;
96                 *dst = comp(pat, *dst, first);
97         } else {
98                 /* Multiple destination words */
99                 /* Leading bits */
100                 if (first) {
101                         *dst = comp(pat, *dst, first);
102                         dst++;
103                         pat = pat << left | pat >> right;
104                         n -= bits - dst_idx;
105                 }
106
107                 /* Main chunk */
108                 n /= bits;
109                 while (n >= 4) {
110                         *dst++ = pat;
111                         pat = pat << left | pat >> right;
112                         *dst++ = pat;
113                         pat = pat << left | pat >> right;
114                         *dst++ = pat;
115                         pat = pat << left | pat >> right;
116                         *dst++ = pat;
117                         pat = pat << left | pat >> right;
118                         n -= 4;
119                 }
120                 while (n--) {
121                         *dst++ = pat;
122                         pat = pat << left | pat >> right;
123                 }
124
125                 /* Trailing bits */
126                 if (last)
127                         *dst = comp(pat, *dst, first);
128         }
129 }
130
131     /*
132      *  Aligned pattern invert using 32/64-bit memory accesses
133      */
134 static void
135 bitfill_aligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
136                     unsigned long pat, unsigned n, int bits)
137 {
138         unsigned long val = pat;
139         unsigned long first, last;
140
141         if (!n)
142                 return;
143
144         first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
145         last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
146
147         if (dst_idx+n <= bits) {
148                 /* Single word */
149                 if (last)
150                         first &= last;
151                 *dst = comp(*dst ^ val, *dst, first);
152         } else {
153                 /* Multiple destination words */
154                 /* Leading bits */
155                 if (first!=0UL) {
156                         *dst = comp(*dst ^ val, *dst, first);
157                         dst++;
158                         n -= bits - dst_idx;
159                 }
160
161                 /* Main chunk */
162                 n /= bits;
163                 while (n >= 8) {
164                         *dst++ ^= val;
165                         *dst++ ^= val;
166                         *dst++ ^= val;
167                         *dst++ ^= val;
168                         *dst++ ^= val;
169                         *dst++ ^= val;
170                         *dst++ ^= val;
171                         *dst++ ^= val;
172                         n -= 8;
173                 }
174                 while (n--)
175                         *dst++ ^= val;
176                 /* Trailing bits */
177                 if (last)
178                         *dst = comp(*dst ^ val, *dst, last);
179         }
180 }
181
182
183     /*
184      *  Unaligned generic pattern invert using 32/64-bit memory accesses
185      *  The pattern must have been expanded to a full 32/64-bit value
186      *  Left/right are the appropriate shifts to convert to the pattern to be
187      *  used for the next 32/64-bit word
188      */
189
190 static void
191 bitfill_unaligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
192                       unsigned long pat, int left, int right, unsigned n,
193                       int bits)
194 {
195         unsigned long first, last;
196
197         if (!n)
198                 return;
199
200         first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
201         last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
202
203         if (dst_idx+n <= bits) {
204                 /* Single word */
205                 if (last)
206                         first &= last;
207                 *dst = comp(*dst ^ pat, *dst, first);
208         } else {
209                 /* Multiple destination words */
210
211                 /* Leading bits */
212                 if (first != 0UL) {
213                         *dst = comp(*dst ^ pat, *dst, first);
214                         dst++;
215                         pat = pat << left | pat >> right;
216                         n -= bits - dst_idx;
217                 }
218
219                 /* Main chunk */
220                 n /= bits;
221                 while (n >= 4) {
222                         *dst++ ^= pat;
223                         pat = pat << left | pat >> right;
224                         *dst++ ^= pat;
225                         pat = pat << left | pat >> right;
226                         *dst++ ^= pat;
227                         pat = pat << left | pat >> right;
228                         *dst++ ^= pat;
229                         pat = pat << left | pat >> right;
230                         n -= 4;
231                 }
232                 while (n--) {
233                         *dst ^= pat;
234                         pat = pat << left | pat >> right;
235                 }
236
237                 /* Trailing bits */
238                 if (last)
239                         *dst = comp(*dst ^ pat, *dst, last);
240         }
241 }
242
243 void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
244 {
245         unsigned long pat, fg;
246         unsigned long width = rect->width, height = rect->height;
247         int bits = BITS_PER_LONG, bytes = bits >> 3;
248         u32 bpp = p->var.bits_per_pixel;
249         unsigned long *dst;
250         int dst_idx, left;
251
252         if (p->state != FBINFO_STATE_RUNNING)
253                 return;
254
255         if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
256             p->fix.visual == FB_VISUAL_DIRECTCOLOR )
257                 fg = ((u32 *) (p->pseudo_palette))[rect->color];
258         else
259                 fg = rect->color;
260
261         pat = pixel_to_pat( bpp, fg);
262
263         dst = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
264         dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
265         dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
266         /* FIXME For now we support 1-32 bpp only */
267         left = bits % bpp;
268         if (p->fbops->fb_sync)
269                 p->fbops->fb_sync(p);
270         if (!left) {
271                 void (*fill_op32)(struct fb_info *p, unsigned long *dst,
272                                   int dst_idx, unsigned long pat, unsigned n,
273                                   int bits) = NULL;
274
275                 switch (rect->rop) {
276                 case ROP_XOR:
277                         fill_op32 = bitfill_aligned_rev;
278                         break;
279                 case ROP_COPY:
280                         fill_op32 = bitfill_aligned;
281                         break;
282                 default:
283                         printk( KERN_ERR "cfb_fillrect(): unknown rop, "
284                                 "defaulting to ROP_COPY\n");
285                         fill_op32 = bitfill_aligned;
286                         break;
287                 }
288                 while (height--) {
289                         dst += dst_idx >> (ffs(bits) - 1);
290                         dst_idx &= (bits - 1);
291                         fill_op32(p, dst, dst_idx, pat, width*bpp, bits);
292                         dst_idx += p->fix.line_length*8;
293                 }
294         } else {
295                 int right;
296                 int r;
297                 int rot = (left-dst_idx) % bpp;
298                 void (*fill_op)(struct fb_info *p, unsigned long *dst,
299                                 int dst_idx, unsigned long pat, int left,
300                                 int right, unsigned n, int bits) = NULL;
301
302                 /* rotate pattern to correct start position */
303                 pat = pat << rot | pat >> (bpp-rot);
304
305                 right = bpp-left;
306                 switch (rect->rop) {
307                 case ROP_XOR:
308                         fill_op = bitfill_unaligned_rev;
309                         break;
310                 case ROP_COPY:
311                         fill_op = bitfill_unaligned;
312                         break;
313                 default:
314                         printk(KERN_ERR "cfb_fillrect(): unknown rop, "
315                                 "defaulting to ROP_COPY\n");
316                         fill_op = bitfill_unaligned;
317                         break;
318                 }
319                 while (height--) {
320                         dst += dst_idx >> (ffs(bits) - 1);
321                         dst_idx &= (bits - 1);
322                         fill_op(p, dst, dst_idx, pat, left, right,
323                                 width*bpp, bits);
324                         r = (p->fix.line_length*8) % bpp;
325                         pat = pat << (bpp-r) | pat >> r;
326                         dst_idx += p->fix.line_length*8;
327                 }
328         }
329 }
330
331 EXPORT_SYMBOL(sys_fillrect);
332
333 MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
334 MODULE_DESCRIPTION("Generic fill rectangle (sys-to-sys)");
335 MODULE_LICENSE("GPL");