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(unsigned long *dst, int dst_idx, unsigned long pat,
26		unsigned n, int bits)
27{
28	unsigned long first, last;
29
30	if (!n)
31		return;
32
33	first = FB_SHIFT_HIGH(~0UL, dst_idx);
34	last = ~(FB_SHIFT_HIGH(~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
80static void
81bitfill_unaligned(unsigned long *dst, int dst_idx, unsigned long pat,
82		  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(~0UL, dst_idx);
90	last = ~(FB_SHIFT_HIGH(~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     */
134static void
135bitfill_aligned_rev(unsigned long *dst, int dst_idx, unsigned long pat,
136		    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(~0UL, dst_idx);
145	last = ~(FB_SHIFT_HIGH(~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
190static void
191bitfill_unaligned_rev(unsigned long *dst, int dst_idx, unsigned long pat,
192			int left, int right, unsigned n, int bits)
193{
194	unsigned long first, last;
195
196	if (!n)
197		return;
198
199	first = FB_SHIFT_HIGH(~0UL, dst_idx);
200	last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
201
202	if (dst_idx+n <= bits) {
203		/* Single word */
204		if (last)
205			first &= last;
206		*dst = comp(*dst ^ pat, *dst, first);
207	} else {
208		/* Multiple destination words */
209
210		/* Leading bits */
211		if (first != 0UL) {
212			*dst = comp(*dst ^ pat, *dst, first);
213			dst++;
214			pat = pat << left | pat >> right;
215			n -= bits - dst_idx;
216		}
217
218		/* Main chunk */
219		n /= bits;
220		while (n >= 4) {
221			*dst++ ^= pat;
222			pat = pat << left | pat >> right;
223			*dst++ ^= pat;
224			pat = pat << left | pat >> right;
225			*dst++ ^= pat;
226			pat = pat << left | pat >> right;
227			*dst++ ^= pat;
228			pat = pat << left | pat >> right;
229			n -= 4;
230		}
231		while (n--) {
232			*dst ^= pat;
233			pat = pat << left | pat >> right;
234		}
235
236		/* Trailing bits */
237		if (last)
238			*dst = comp(*dst ^ pat, *dst, last);
239	}
240}
241
242void sys_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
243{
244	unsigned long pat, fg;
245	unsigned long width = rect->width, height = rect->height;
246	int bits = BITS_PER_LONG, bytes = bits >> 3;
247	u32 bpp = p->var.bits_per_pixel;
248	unsigned long *dst;
249	int dst_idx, left;
250
251	if (p->state != FBINFO_STATE_RUNNING)
252		return;
253
254	if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
255	    p->fix.visual == FB_VISUAL_DIRECTCOLOR )
256		fg = ((u32 *) (p->pseudo_palette))[rect->color];
257	else
258		fg = rect->color;
259
260	pat = pixel_to_pat( bpp, fg);
261
262	dst = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
263	dst_idx = ((unsigned long)p->screen_base & (bytes - 1))*8;
264	dst_idx += rect->dy*p->fix.line_length*8+rect->dx*bpp;
265	left = bits % bpp;
266	if (p->fbops->fb_sync)
267		p->fbops->fb_sync(p);
268	if (!left) {
269		void (*fill_op32)(unsigned long *dst, int dst_idx,
270		                  unsigned long pat, unsigned n, int bits) =
271			NULL;
272
273		switch (rect->rop) {
274		case ROP_XOR:
275			fill_op32 = bitfill_aligned_rev;
276			break;
277		case ROP_COPY:
278			fill_op32 = bitfill_aligned;
279			break;
280		default:
281			printk( KERN_ERR "cfb_fillrect(): unknown rop, "
282				"defaulting to ROP_COPY\n");
283			fill_op32 = bitfill_aligned;
284			break;
285		}
286		while (height--) {
287			dst += dst_idx >> (ffs(bits) - 1);
288			dst_idx &= (bits - 1);
289			fill_op32(dst, dst_idx, pat, width*bpp, bits);
290			dst_idx += p->fix.line_length*8;
291		}
292	} else {
293		int right;
294		int r;
295		int rot = (left-dst_idx) % bpp;
296		void (*fill_op)(unsigned long *dst, int dst_idx,
297		                unsigned long pat, int left, int right,
298		                unsigned n, int bits) = NULL;
299
300		/* rotate pattern to correct start position */
301		pat = pat << rot | pat >> (bpp-rot);
302
303		right = bpp-left;
304		switch (rect->rop) {
305		case ROP_XOR:
306			fill_op = bitfill_unaligned_rev;
307			break;
308		case ROP_COPY:
309			fill_op = bitfill_unaligned;
310			break;
311		default:
312			printk(KERN_ERR "cfb_fillrect(): unknown rop, "
313				"defaulting to ROP_COPY\n");
314			fill_op = bitfill_unaligned;
315			break;
316		}
317		while (height--) {
318			dst += dst_idx >> (ffs(bits) - 1);
319			dst_idx &= (bits - 1);
320			fill_op(dst, dst_idx, pat, left, right,
321				width*bpp, bits);
322			r = (p->fix.line_length*8) % bpp;
323			pat = pat << (bpp-r) | pat >> r;
324			dst_idx += p->fix.line_length*8;
325		}
326	}
327}
328
329EXPORT_SYMBOL(sys_fillrect);
330
331MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
332MODULE_DESCRIPTION("Generic fill rectangle (sys-to-sys)");
333MODULE_LICENSE("GPL");
334