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
38static void
39bitfill_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
94static void
95bitfill_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     */
148static void
149bitfill_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
217static void
218bitfill_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
278void 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	left = bits % bpp;
302	if (p->fbops->fb_sync)
303		p->fbops->fb_sync(p);
304	if (!left) {
305		void (*fill_op32)(unsigned long __iomem *dst, int dst_idx,
306		                  unsigned long pat, unsigned n, int bits) = NULL;
307
308		switch (rect->rop) {
309		case ROP_XOR:
310			fill_op32 = bitfill_aligned_rev;
311			break;
312		case ROP_COPY:
313			fill_op32 = bitfill_aligned;
314			break;
315		default:
316			printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
317			fill_op32 = bitfill_aligned;
318			break;
319		}
320		while (height--) {
321			dst += dst_idx >> (ffs(bits) - 1);
322			dst_idx &= (bits - 1);
323			fill_op32(dst, dst_idx, pat, width*bpp, bits);
324			dst_idx += p->fix.line_length*8;
325		}
326	} else {
327		int right;
328		int r;
329		int rot = (left-dst_idx) % bpp;
330		void (*fill_op)(unsigned long __iomem *dst, int dst_idx,
331		                unsigned long pat, int left, int right,
332		                unsigned n, int bits) = NULL;
333
334		/* rotate pattern to correct start position */
335		pat = pat << rot | pat >> (bpp-rot);
336
337		right = bpp-left;
338		switch (rect->rop) {
339		case ROP_XOR:
340			fill_op = bitfill_unaligned_rev;
341			break;
342		case ROP_COPY:
343			fill_op = bitfill_unaligned;
344			break;
345		default:
346			printk( KERN_ERR "cfb_fillrect(): unknown rop, defaulting to ROP_COPY\n");
347			fill_op = bitfill_unaligned;
348			break;
349		}
350		while (height--) {
351			dst += dst_idx >> (ffs(bits) - 1);
352			dst_idx &= (bits - 1);
353			fill_op(dst, dst_idx, pat, left, right,
354				width*bpp, bits);
355			r = (p->fix.line_length*8) % bpp;
356			pat = pat << (bpp-r) | pat >> r;
357			dst_idx += p->fix.line_length*8;
358		}
359	}
360}
361
362EXPORT_SYMBOL(cfb_fillrect);
363
364MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
365MODULE_DESCRIPTION("Generic software accelerated fill rectangle");
366MODULE_LICENSE("GPL");
367