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
24static void
25bitfill_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		memset_l(dst, pat, n);
54		dst += n;
55
56		/* Trailing bits */
57		if (last)
58			*dst = comp(pat, *dst, last);
59	}
60}
61
62
63    /*
64     *  Unaligned generic pattern fill using 32/64-bit memory accesses
65     *  The pattern must have been expanded to a full 32/64-bit value
66     *  Left/right are the appropriate shifts to convert to the pattern to be
67     *  used for the next 32/64-bit word
68     */
69
70static void
71bitfill_unaligned(struct fb_info *p, unsigned long *dst, int dst_idx,
72		  unsigned long pat, int left, int right, unsigned n, int bits)
73{
74	unsigned long first, last;
75
76	if (!n)
77		return;
78
79	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
80	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
81
82	if (dst_idx+n <= bits) {
83		/* Single word */
84		if (last)
85			first &= last;
86		*dst = comp(pat, *dst, first);
87	} else {
88		/* Multiple destination words */
89		/* Leading bits */
90		if (first) {
91			*dst = comp(pat, *dst, first);
92			dst++;
93			pat = pat << left | pat >> right;
94			n -= bits - dst_idx;
95		}
96
97		/* Main chunk */
98		n /= bits;
99		while (n >= 4) {
100			*dst++ = pat;
101			pat = pat << left | pat >> right;
102			*dst++ = pat;
103			pat = pat << left | pat >> right;
104			*dst++ = pat;
105			pat = pat << left | pat >> right;
106			*dst++ = pat;
107			pat = pat << left | pat >> right;
108			n -= 4;
109		}
110		while (n--) {
111			*dst++ = pat;
112			pat = pat << left | pat >> right;
113		}
114
115		/* Trailing bits */
116		if (last)
117			*dst = comp(pat, *dst, last);
118	}
119}
120
121    /*
122     *  Aligned pattern invert using 32/64-bit memory accesses
123     */
124static void
125bitfill_aligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
126		    unsigned long pat, unsigned n, int bits)
127{
128	unsigned long val = pat;
129	unsigned long first, last;
130
131	if (!n)
132		return;
133
134	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
135	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
136
137	if (dst_idx+n <= bits) {
138		/* Single word */
139		if (last)
140			first &= last;
141		*dst = comp(*dst ^ val, *dst, first);
142	} else {
143		/* Multiple destination words */
144		/* Leading bits */
145		if (first!=0UL) {
146			*dst = comp(*dst ^ val, *dst, first);
147			dst++;
148			n -= bits - dst_idx;
149		}
150
151		/* Main chunk */
152		n /= bits;
153		while (n >= 8) {
154			*dst++ ^= val;
155			*dst++ ^= val;
156			*dst++ ^= val;
157			*dst++ ^= val;
158			*dst++ ^= val;
159			*dst++ ^= val;
160			*dst++ ^= val;
161			*dst++ ^= val;
162			n -= 8;
163		}
164		while (n--)
165			*dst++ ^= val;
166		/* Trailing bits */
167		if (last)
168			*dst = comp(*dst ^ val, *dst, last);
169	}
170}
171
172
173    /*
174     *  Unaligned generic pattern invert using 32/64-bit memory accesses
175     *  The pattern must have been expanded to a full 32/64-bit value
176     *  Left/right are the appropriate shifts to convert to the pattern to be
177     *  used for the next 32/64-bit word
178     */
179
180static void
181bitfill_unaligned_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
182		      unsigned long pat, int left, int right, unsigned n,
183		      int bits)
184{
185	unsigned long first, last;
186
187	if (!n)
188		return;
189
190	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
191	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
192
193	if (dst_idx+n <= bits) {
194		/* Single word */
195		if (last)
196			first &= last;
197		*dst = comp(*dst ^ pat, *dst, first);
198	} else {
199		/* Multiple destination words */
200
201		/* Leading bits */
202		if (first != 0UL) {
203			*dst = comp(*dst ^ pat, *dst, first);
204			dst++;
205			pat = pat << left | pat >> right;
206			n -= bits - dst_idx;
207		}
208
209		/* Main chunk */
210		n /= bits;
211		while (n >= 4) {
212			*dst++ ^= pat;
213			pat = pat << left | pat >> right;
214			*dst++ ^= pat;
215			pat = pat << left | pat >> right;
216			*dst++ ^= pat;
217			pat = pat << left | pat >> right;
218			*dst++ ^= pat;
219			pat = pat << left | pat >> right;
220			n -= 4;
221		}
222		while (n--) {
223			*dst ^= pat;
224			pat = pat << left | pat >> right;
225		}
226
227		/* Trailing bits */
228		if (last)
229			*dst = comp(*dst ^ pat, *dst, last);
230	}
231}
232
233void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
234{
235	unsigned long pat, pat2, fg;
236	unsigned long width = rect->width, height = rect->height;
237	int bits = BITS_PER_LONG, bytes = bits >> 3;
238	u32 bpp = p->var.bits_per_pixel;
239	unsigned long *dst;
240	int dst_idx, left;
241
242	if (p->state != FBINFO_STATE_RUNNING)
243		return;
244
245	if (!(p->flags & FBINFO_VIRTFB))
246		fb_warn_once(p, "Framebuffer is not in virtual address space.");
247
248	if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
249	    p->fix.visual == FB_VISUAL_DIRECTCOLOR )
250		fg = ((u32 *) (p->pseudo_palette))[rect->color];
251	else
252		fg = rect->color;
253
254	pat = pixel_to_pat( bpp, fg);
255
256	dst = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
257	dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
258	dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
259	/* FIXME For now we support 1-32 bpp only */
260	left = bits % bpp;
261	if (p->fbops->fb_sync)
262		p->fbops->fb_sync(p);
263	if (!left) {
264		void (*fill_op32)(struct fb_info *p, unsigned long *dst,
265				  int dst_idx, unsigned long pat, unsigned n,
266				  int bits) = NULL;
267
268		switch (rect->rop) {
269		case ROP_XOR:
270			fill_op32 = bitfill_aligned_rev;
271			break;
272		case ROP_COPY:
273			fill_op32 = bitfill_aligned;
274			break;
275		default:
276			printk( KERN_ERR "cfb_fillrect(): unknown rop, "
277				"defaulting to ROP_COPY\n");
278			fill_op32 = bitfill_aligned;
279			break;
280		}
281		while (height--) {
282			dst += dst_idx >> (ffs(bits) - 1);
283			dst_idx &= (bits - 1);
284			fill_op32(p, dst, dst_idx, pat, width*bpp, bits);
285			dst_idx += p->fix.line_length*8;
286		}
287	} else {
288		int right, r;
289		void (*fill_op)(struct fb_info *p, unsigned long *dst,
290				int dst_idx, unsigned long pat, int left,
291				int right, unsigned n, int bits) = NULL;
292#ifdef __LITTLE_ENDIAN
293		right = left;
294		left = bpp - right;
295#else
296		right = bpp - left;
297#endif
298		switch (rect->rop) {
299		case ROP_XOR:
300			fill_op = bitfill_unaligned_rev;
301			break;
302		case ROP_COPY:
303			fill_op = bitfill_unaligned;
304			break;
305		default:
306			printk(KERN_ERR "sys_fillrect(): unknown rop, "
307				"defaulting to ROP_COPY\n");
308			fill_op = bitfill_unaligned;
309			break;
310		}
311		while (height--) {
312			dst += dst_idx / bits;
313			dst_idx &= (bits - 1);
314			r = dst_idx % bpp;
315			/* rotate pattern to the correct start position */
316			pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
317			fill_op(p, dst, dst_idx, pat2, left, right,
318				width*bpp, bits);
319			dst_idx += p->fix.line_length*8;
320		}
321	}
322}
323
324EXPORT_SYMBOL(sys_fillrect);
325
326MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
327MODULE_DESCRIPTION("Generic fill rectangle (sys-to-sys)");
328MODULE_LICENSE("GPL");
329