1 /*
2  *  Generic Bit Block Transfer for frame buffers located in system RAM with
3  *  packed pixels of any depth.
4  *
5  *  Based almost entirely from cfbcopyarea.c (which is based almost entirely
6  *  on Geert Uytterhoeven's copyarea 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  */
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 #include <linux/string.h>
18 #include <linux/fb.h>
19 #include <asm/types.h>
20 #include <asm/io.h>
21 #include "fb_draw.h"
22 
23     /*
24      *  Generic bitwise copy algorithm
25      */
26 
27 static void
bitcpy(struct fb_info * p,unsigned long * dst,int dst_idx,const unsigned long * src,int src_idx,int bits,unsigned n)28 bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx,
29 		const unsigned long *src, int src_idx, int bits, unsigned n)
30 {
31 	unsigned long first, last;
32 	int const shift = dst_idx-src_idx;
33 	int left, right;
34 
35 	first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
36 	last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
37 
38 	if (!shift) {
39 		/* Same alignment for source and dest */
40 		if (dst_idx+n <= bits) {
41 			/* Single word */
42 			if (last)
43 				first &= last;
44 			*dst = comp(*src, *dst, first);
45 		} else {
46 			/* Multiple destination words */
47 			/* Leading bits */
48  			if (first != ~0UL) {
49 				*dst = comp(*src, *dst, first);
50 				dst++;
51 				src++;
52 				n -= bits - dst_idx;
53 			}
54 
55 			/* Main chunk */
56 			n /= bits;
57 			while (n >= 8) {
58 				*dst++ = *src++;
59 				*dst++ = *src++;
60 				*dst++ = *src++;
61 				*dst++ = *src++;
62 				*dst++ = *src++;
63 				*dst++ = *src++;
64 				*dst++ = *src++;
65 				*dst++ = *src++;
66 				n -= 8;
67 			}
68 			while (n--)
69 				*dst++ = *src++;
70 
71 			/* Trailing bits */
72 			if (last)
73 				*dst = comp(*src, *dst, last);
74 		}
75 	} else {
76 		unsigned long d0, d1;
77 		int m;
78 
79 		/* Different alignment for source and dest */
80 		right = shift & (bits - 1);
81 		left = -shift & (bits - 1);
82 
83 		if (dst_idx+n <= bits) {
84 			/* Single destination word */
85 			if (last)
86 				first &= last;
87 			if (shift > 0) {
88 				/* Single source word */
89 				*dst = comp(*src >> right, *dst, first);
90 			} else if (src_idx+n <= bits) {
91 				/* Single source word */
92 				*dst = comp(*src << left, *dst, first);
93 			} else {
94 				/* 2 source words */
95 				d0 = *src++;
96 				d1 = *src;
97 				*dst = comp(d0 << left | d1 >> right, *dst,
98 					    first);
99 			}
100 		} else {
101 			/* Multiple destination words */
102 			/** We must always remember the last value read,
103 			    because in case SRC and DST overlap bitwise (e.g.
104 			    when moving just one pixel in 1bpp), we always
105 			    collect one full long for DST and that might
106 			    overlap with the current long from SRC. We store
107 			    this value in 'd0'. */
108 			d0 = *src++;
109 			/* Leading bits */
110 			if (shift > 0) {
111 				/* Single source word */
112 				*dst = comp(d0 >> right, *dst, first);
113 				dst++;
114 				n -= bits - dst_idx;
115 			} else {
116 				/* 2 source words */
117 				d1 = *src++;
118 				*dst = comp(d0 << left | *dst >> right, *dst, first);
119 				d0 = d1;
120 				dst++;
121 				n -= bits - dst_idx;
122 			}
123 
124 			/* Main chunk */
125 			m = n % bits;
126 			n /= bits;
127 			while (n >= 4) {
128 				d1 = *src++;
129 				*dst++ = d0 << left | d1 >> right;
130 				d0 = d1;
131 				d1 = *src++;
132 				*dst++ = d0 << left | d1 >> right;
133 				d0 = d1;
134 				d1 = *src++;
135 				*dst++ = d0 << left | d1 >> right;
136 				d0 = d1;
137 				d1 = *src++;
138 				*dst++ = d0 << left | d1 >> right;
139 				d0 = d1;
140 				n -= 4;
141 			}
142 			while (n--) {
143 				d1 = *src++;
144 				*dst++ = d0 << left | d1 >> right;
145 				d0 = d1;
146 			}
147 
148 			/* Trailing bits */
149 			if (last) {
150 				if (m <= right) {
151 					/* Single source word */
152 					*dst = comp(d0 << left, *dst, last);
153 				} else {
154 					/* 2 source words */
155  					d1 = *src;
156 					*dst = comp(d0 << left | d1 >> right,
157 						    *dst, last);
158 				}
159 			}
160 		}
161 	}
162 }
163 
164     /*
165      *  Generic bitwise copy algorithm, operating backward
166      */
167 
168 static void
bitcpy_rev(struct fb_info * p,unsigned long * dst,int dst_idx,const unsigned long * src,int src_idx,int bits,unsigned n)169 bitcpy_rev(struct fb_info *p, unsigned long *dst, int dst_idx,
170 		const unsigned long *src, int src_idx, int bits, unsigned n)
171 {
172 	unsigned long first, last;
173 	int shift;
174 
175 	dst += (n-1)/bits;
176 	src += (n-1)/bits;
177 	if ((n-1) % bits) {
178 		dst_idx += (n-1) % bits;
179 		dst += dst_idx >> (ffs(bits) - 1);
180 		dst_idx &= bits - 1;
181 		src_idx += (n-1) % bits;
182 		src += src_idx >> (ffs(bits) - 1);
183 		src_idx &= bits - 1;
184 	}
185 
186 	shift = dst_idx-src_idx;
187 
188 	first = FB_SHIFT_LOW(p, ~0UL, bits - 1 - dst_idx);
189 	last = ~(FB_SHIFT_LOW(p, ~0UL, bits - 1 - ((dst_idx-n) % bits)));
190 
191 	if (!shift) {
192 		/* Same alignment for source and dest */
193 		if ((unsigned long)dst_idx+1 >= n) {
194 			/* Single word */
195 			if (last)
196 				first &= last;
197 			*dst = comp(*src, *dst, first);
198 		} else {
199 			/* Multiple destination words */
200 
201 			/* Leading bits */
202 			if (first != ~0UL) {
203 				*dst = comp(*src, *dst, first);
204 				dst--;
205 				src--;
206 				n -= dst_idx+1;
207 			}
208 
209 			/* Main chunk */
210 			n /= bits;
211 			while (n >= 8) {
212 				*dst-- = *src--;
213 				*dst-- = *src--;
214 				*dst-- = *src--;
215 				*dst-- = *src--;
216 				*dst-- = *src--;
217 				*dst-- = *src--;
218 				*dst-- = *src--;
219 				*dst-- = *src--;
220 				n -= 8;
221 			}
222 			while (n--)
223 				*dst-- = *src--;
224 			/* Trailing bits */
225 			if (last)
226 				*dst = comp(*src, *dst, last);
227 		}
228 	} else {
229 		/* Different alignment for source and dest */
230 
231 		int const left = -shift & (bits-1);
232 		int const right = shift & (bits-1);
233 
234 		if ((unsigned long)dst_idx+1 >= n) {
235 			/* Single destination word */
236 			if (last)
237 				first &= last;
238 			if (shift < 0) {
239 				/* Single source word */
240 				*dst = comp(*src << left, *dst, first);
241 			} else if (1+(unsigned long)src_idx >= n) {
242 				/* Single source word */
243 				*dst = comp(*src >> right, *dst, first);
244 			} else {
245 				/* 2 source words */
246 				*dst = comp(*src >> right | *(src-1) << left,
247 					    *dst, first);
248 			}
249 		} else {
250 			/* Multiple destination words */
251 			/** We must always remember the last value read,
252 			    because in case SRC and DST overlap bitwise (e.g.
253 			    when moving just one pixel in 1bpp), we always
254 			    collect one full long for DST and that might
255 			    overlap with the current long from SRC. We store
256 			    this value in 'd0'. */
257 			unsigned long d0, d1;
258 			int m;
259 
260 			d0 = *src--;
261 			/* Leading bits */
262 			if (shift < 0) {
263 				/* Single source word */
264 				*dst = comp(d0 << left, *dst, first);
265 			} else {
266 				/* 2 source words */
267 				d1 = *src--;
268 				*dst = comp(d0 >> right | d1 << left, *dst,
269 					    first);
270 				d0 = d1;
271 			}
272 			dst--;
273 			n -= dst_idx+1;
274 
275 			/* Main chunk */
276 			m = n % bits;
277 			n /= bits;
278 			while (n >= 4) {
279 				d1 = *src--;
280 				*dst-- = d0 >> right | d1 << left;
281 				d0 = d1;
282 				d1 = *src--;
283 				*dst-- = d0 >> right | d1 << left;
284 				d0 = d1;
285 				d1 = *src--;
286 				*dst-- = d0 >> right | d1 << left;
287 				d0 = d1;
288 				d1 = *src--;
289 				*dst-- = d0 >> right | d1 << left;
290 				d0 = d1;
291 				n -= 4;
292 			}
293 			while (n--) {
294 				d1 = *src--;
295 				*dst-- = d0 >> right | d1 << left;
296 				d0 = d1;
297 			}
298 
299 			/* Trailing bits */
300 			if (last) {
301 				if (m <= left) {
302 					/* Single source word */
303 					*dst = comp(d0 >> right, *dst, last);
304 				} else {
305 					/* 2 source words */
306 					d1 = *src;
307 					*dst = comp(d0 >> right | d1 << left,
308 						    *dst, last);
309 				}
310 			}
311 		}
312 	}
313 }
314 
sys_copyarea(struct fb_info * p,const struct fb_copyarea * area)315 void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area)
316 {
317 	u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
318 	u32 height = area->height, width = area->width;
319 	unsigned long const bits_per_line = p->fix.line_length*8u;
320 	unsigned long *dst = NULL, *src = NULL;
321 	int bits = BITS_PER_LONG, bytes = bits >> 3;
322 	int dst_idx = 0, src_idx = 0, rev_copy = 0;
323 
324 	if (p->state != FBINFO_STATE_RUNNING)
325 		return;
326 
327 	/* if the beginning of the target area might overlap with the end of
328 	the source area, be have to copy the area reverse. */
329 	if ((dy == sy && dx > sx) || (dy > sy)) {
330 		dy += height;
331 		sy += height;
332 		rev_copy = 1;
333 	}
334 
335 	/* split the base of the framebuffer into a long-aligned address and
336 	   the index of the first bit */
337 	dst = src = (unsigned long *)((unsigned long)p->screen_base &
338 				      ~(bytes-1));
339 	dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
340 	/* add offset of source and target area */
341 	dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
342 	src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
343 
344 	if (p->fbops->fb_sync)
345 		p->fbops->fb_sync(p);
346 
347 	if (rev_copy) {
348 		while (height--) {
349 			dst_idx -= bits_per_line;
350 			src_idx -= bits_per_line;
351 			dst += dst_idx >> (ffs(bits) - 1);
352 			dst_idx &= (bytes - 1);
353 			src += src_idx >> (ffs(bits) - 1);
354 			src_idx &= (bytes - 1);
355 			bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
356 				width*p->var.bits_per_pixel);
357 		}
358 	} else {
359 		while (height--) {
360 			dst += dst_idx >> (ffs(bits) - 1);
361 			dst_idx &= (bytes - 1);
362 			src += src_idx >> (ffs(bits) - 1);
363 			src_idx &= (bytes - 1);
364 			bitcpy(p, dst, dst_idx, src, src_idx, bits,
365 				width*p->var.bits_per_pixel);
366 			dst_idx += bits_per_line;
367 			src_idx += bits_per_line;
368 		}
369 	}
370 }
371 
372 EXPORT_SYMBOL(sys_copyarea);
373 
374 MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
375 MODULE_DESCRIPTION("Generic copyarea (sys-to-sys)");
376 MODULE_LICENSE("GPL");
377 
378