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, last);
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, pat2, 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, r;
296 		void (*fill_op)(struct fb_info *p, unsigned long *dst,
297 				int dst_idx, unsigned long pat, int left,
298 				int right, unsigned n, int bits) = NULL;
299 #ifdef __LITTLE_ENDIAN
300 		right = left;
301 		left = bpp - right;
302 #else
303 		right = bpp - left;
304 #endif
305 		switch (rect->rop) {
306 		case ROP_XOR:
307 			fill_op = bitfill_unaligned_rev;
308 			break;
309 		case ROP_COPY:
310 			fill_op = bitfill_unaligned;
311 			break;
312 		default:
313 			printk(KERN_ERR "sys_fillrect(): unknown rop, "
314 				"defaulting to ROP_COPY\n");
315 			fill_op = bitfill_unaligned;
316 			break;
317 		}
318 		while (height--) {
319 			dst += dst_idx / bits;
320 			dst_idx &= (bits - 1);
321 			r = dst_idx % bpp;
322 			/* rotate pattern to the correct start position */
323 			pat2 = le_long_to_cpu(rolx(cpu_to_le_long(pat), r, bpp));
324 			fill_op(p, dst, dst_idx, pat2, left, right,
325 				width*bpp, bits);
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");
336