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
27static void
28bitcpy(struct fb_info *p, unsigned long *dst, unsigned dst_idx,
29	const unsigned long *src, unsigned 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 << left, *dst, first);
90			} else if (src_idx+n <= bits) {
91				/* Single source word */
92				*dst = comp(*src >> right, *dst, first);
93			} else {
94				/* 2 source words */
95				d0 = *src++;
96				d1 = *src;
97				*dst = comp(d0 >> right | d1 << left, *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 << left, *dst, first);
113				dst++;
114				n -= bits - dst_idx;
115			} else {
116				/* 2 source words */
117				d1 = *src++;
118				*dst = comp(d0 >> right | d1 << left, *dst,
119					    first);
120				d0 = d1;
121				dst++;
122				n -= bits - dst_idx;
123			}
124
125			/* Main chunk */
126			m = n % bits;
127			n /= bits;
128			while (n >= 4) {
129				d1 = *src++;
130				*dst++ = d0 >> right | d1 << left;
131				d0 = d1;
132				d1 = *src++;
133				*dst++ = d0 >> right | d1 << left;
134				d0 = d1;
135				d1 = *src++;
136				*dst++ = d0 >> right | d1 << left;
137				d0 = d1;
138				d1 = *src++;
139				*dst++ = d0 >> right | d1 << left;
140				d0 = d1;
141				n -= 4;
142			}
143			while (n--) {
144				d1 = *src++;
145				*dst++ = d0 >> right | d1 << left;
146				d0 = d1;
147			}
148
149			/* Trailing bits */
150			if (m) {
151				if (m <= bits - right) {
152					/* Single source word */
153					d0 >>= right;
154				} else {
155					/* 2 source words */
156 					d1 = *src;
157					d0 = d0 >> right | d1 << left;
158				}
159				*dst = comp(d0, *dst, last);
160			}
161		}
162	}
163}
164
165    /*
166     *  Generic bitwise copy algorithm, operating backward
167     */
168
169static void
170bitcpy_rev(struct fb_info *p, unsigned long *dst, unsigned dst_idx,
171	   const unsigned long *src, unsigned src_idx, unsigned bits,
172	   unsigned n)
173{
174	unsigned long first, last;
175	int shift;
176
177	dst += (dst_idx + n - 1) / bits;
178	src += (src_idx + n - 1) / bits;
179	dst_idx = (dst_idx + n - 1) % bits;
180	src_idx = (src_idx + n - 1) % bits;
181
182	shift = dst_idx-src_idx;
183
184	first = ~FB_SHIFT_HIGH(p, ~0UL, (dst_idx + 1) % bits);
185	last = FB_SHIFT_HIGH(p, ~0UL, (bits + dst_idx + 1 - n) % bits);
186
187	if (!shift) {
188		/* Same alignment for source and dest */
189		if ((unsigned long)dst_idx+1 >= n) {
190			/* Single word */
191			if (first)
192				last &= first;
193			*dst = comp(*src, *dst, last);
194		} else {
195			/* Multiple destination words */
196
197			/* Leading bits */
198			if (first) {
199				*dst = comp(*src, *dst, first);
200				dst--;
201				src--;
202				n -= dst_idx+1;
203			}
204
205			/* Main chunk */
206			n /= bits;
207			while (n >= 8) {
208				*dst-- = *src--;
209				*dst-- = *src--;
210				*dst-- = *src--;
211				*dst-- = *src--;
212				*dst-- = *src--;
213				*dst-- = *src--;
214				*dst-- = *src--;
215				*dst-- = *src--;
216				n -= 8;
217			}
218			while (n--)
219				*dst-- = *src--;
220			/* Trailing bits */
221			if (last != -1UL)
222				*dst = comp(*src, *dst, last);
223		}
224	} else {
225		/* Different alignment for source and dest */
226
227		int const left = shift & (bits-1);
228		int const right = -shift & (bits-1);
229
230		if ((unsigned long)dst_idx+1 >= n) {
231			/* Single destination word */
232			if (first)
233				last &= first;
234			if (shift < 0) {
235				/* Single source word */
236				*dst = comp(*src >> right, *dst, last);
237			} else if (1+(unsigned long)src_idx >= n) {
238				/* Single source word */
239				*dst = comp(*src << left, *dst, last);
240			} else {
241				/* 2 source words */
242				*dst = comp(*src << left | *(src-1) >> right,
243					    *dst, last);
244			}
245		} else {
246			/* Multiple destination words */
247			/** We must always remember the last value read,
248			    because in case SRC and DST overlap bitwise (e.g.
249			    when moving just one pixel in 1bpp), we always
250			    collect one full long for DST and that might
251			    overlap with the current long from SRC. We store
252			    this value in 'd0'. */
253			unsigned long d0, d1;
254			int m;
255
256			d0 = *src--;
257			/* Leading bits */
258			if (shift < 0) {
259				/* Single source word */
260				d1 = d0;
261				d0 >>= right;
262			} else {
263				/* 2 source words */
264				d1 = *src--;
265				d0 = d0 << left | d1 >> right;
266			}
267			if (!first)
268				*dst = d0;
269			else
270				*dst = comp(d0, *dst, first);
271			d0 = d1;
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 << left | d1 >> right;
281				d0 = d1;
282				d1 = *src--;
283				*dst-- = d0 << left | d1 >> right;
284				d0 = d1;
285				d1 = *src--;
286				*dst-- = d0 << left | d1 >> right;
287				d0 = d1;
288				d1 = *src--;
289				*dst-- = d0 << left | d1 >> right;
290				d0 = d1;
291				n -= 4;
292			}
293			while (n--) {
294				d1 = *src--;
295				*dst-- = d0 << left | d1 >> right;
296				d0 = d1;
297			}
298
299			/* Trailing bits */
300			if (m) {
301				if (m <= bits - left) {
302					/* Single source word */
303					d0 <<= left;
304				} else {
305					/* 2 source words */
306					d1 = *src;
307					d0 = d0 << left | d1 >> right;
308				}
309				*dst = comp(d0, *dst, last);
310			}
311		}
312	}
313}
314
315void 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 *base = NULL;
321	int bits = BITS_PER_LONG, bytes = bits >> 3;
322	unsigned 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	base = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
338	dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
339	/* add offset of source and target area */
340	dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
341	src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
342
343	if (p->fbops->fb_sync)
344		p->fbops->fb_sync(p);
345
346	if (rev_copy) {
347		while (height--) {
348			dst_idx -= bits_per_line;
349			src_idx -= bits_per_line;
350			bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
351				base + (src_idx / bits), src_idx % bits, bits,
352				width*p->var.bits_per_pixel);
353		}
354	} else {
355		while (height--) {
356			bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
357				base + (src_idx / bits), src_idx % bits, bits,
358				width*p->var.bits_per_pixel);
359			dst_idx += bits_per_line;
360			src_idx += bits_per_line;
361		}
362	}
363}
364
365EXPORT_SYMBOL(sys_copyarea);
366
367MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
368MODULE_DESCRIPTION("Generic copyarea (sys-to-sys)");
369MODULE_LICENSE("GPL");
370
371