1/*
2 *  Generic function for frame buffer with packed pixels of any depth.
3 *
4 *      Copyright (C)  1999-2005 James Simmons <jsimmons@www.infradead.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 *  This is for cfb packed pixels. Iplan and such are incorporated in the
13 *  drivers that need them.
14 *
15 *  FIXME
16 *
17 *  Also need to add code to deal with cards endians that are different than
18 *  the native cpu endians. I also need to deal with MSB position in the word.
19 *
20 *  The two functions or copying forward and backward could be split up like
21 *  the ones for filling, i.e. in aligned and unaligned versions. This would
22 *  help moving some redundant computations and branches out of the loop, too.
23 */
24
25#include <linux/module.h>
26#include <linux/kernel.h>
27#include <linux/string.h>
28#include <linux/fb.h>
29#include <asm/types.h>
30#include <asm/io.h>
31#include "fb_draw.h"
32
33#if BITS_PER_LONG == 32
34#  define FB_WRITEL fb_writel
35#  define FB_READL  fb_readl
36#else
37#  define FB_WRITEL fb_writeq
38#  define FB_READL  fb_readq
39#endif
40
41    /*
42     *  Generic bitwise copy algorithm
43     */
44
45static void
46bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
47		const unsigned long __iomem *src, unsigned src_idx, int bits,
48		unsigned n, u32 bswapmask)
49{
50	unsigned long first, last;
51	int const shift = dst_idx-src_idx;
52
53#if 0
54	/*
55	 * If you suspect bug in this function, compare it with this simple
56	 * memmove implementation.
57	 */
58	memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
59		(char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
60	return;
61#endif
62
63	first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
64	last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
65
66	if (!shift) {
67		// Same alignment for source and dest
68
69		if (dst_idx+n <= bits) {
70			// Single word
71			if (last)
72				first &= last;
73			FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
74		} else {
75			// Multiple destination words
76
77			// Leading bits
78			if (first != ~0UL) {
79				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
80				dst++;
81				src++;
82				n -= bits - dst_idx;
83			}
84
85			// Main chunk
86			n /= bits;
87			while (n >= 8) {
88				FB_WRITEL(FB_READL(src++), dst++);
89				FB_WRITEL(FB_READL(src++), dst++);
90				FB_WRITEL(FB_READL(src++), dst++);
91				FB_WRITEL(FB_READL(src++), dst++);
92				FB_WRITEL(FB_READL(src++), dst++);
93				FB_WRITEL(FB_READL(src++), dst++);
94				FB_WRITEL(FB_READL(src++), dst++);
95				FB_WRITEL(FB_READL(src++), dst++);
96				n -= 8;
97			}
98			while (n--)
99				FB_WRITEL(FB_READL(src++), dst++);
100
101			// Trailing bits
102			if (last)
103				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
104		}
105	} else {
106		/* Different alignment for source and dest */
107		unsigned long d0, d1;
108		int m;
109
110		int const left = shift & (bits - 1);
111		int const right = -shift & (bits - 1);
112
113		if (dst_idx+n <= bits) {
114			// Single destination word
115			if (last)
116				first &= last;
117			d0 = FB_READL(src);
118			d0 = fb_rev_pixels_in_long(d0, bswapmask);
119			if (shift > 0) {
120				// Single source word
121				d0 <<= left;
122			} else if (src_idx+n <= bits) {
123				// Single source word
124				d0 >>= right;
125			} else {
126				// 2 source words
127				d1 = FB_READL(src + 1);
128				d1 = fb_rev_pixels_in_long(d1, bswapmask);
129				d0 = d0 >> right | d1 << left;
130			}
131			d0 = fb_rev_pixels_in_long(d0, bswapmask);
132			FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
133		} else {
134			// Multiple destination words
135			/** We must always remember the last value read, because in case
136			SRC and DST overlap bitwise (e.g. when moving just one pixel in
137			1bpp), we always collect one full long for DST and that might
138			overlap with the current long from SRC. We store this value in
139			'd0'. */
140			d0 = FB_READL(src++);
141			d0 = fb_rev_pixels_in_long(d0, bswapmask);
142			// Leading bits
143			if (shift > 0) {
144				// Single source word
145				d1 = d0;
146				d0 <<= left;
147				n -= bits - dst_idx;
148			} else {
149				// 2 source words
150				d1 = FB_READL(src++);
151				d1 = fb_rev_pixels_in_long(d1, bswapmask);
152
153				d0 = d0 >> right | d1 << left;
154				n -= bits - dst_idx;
155			}
156			d0 = fb_rev_pixels_in_long(d0, bswapmask);
157			FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
158			d0 = d1;
159			dst++;
160
161			// Main chunk
162			m = n % bits;
163			n /= bits;
164			while ((n >= 4) && !bswapmask) {
165				d1 = FB_READL(src++);
166				FB_WRITEL(d0 >> right | d1 << left, dst++);
167				d0 = d1;
168				d1 = FB_READL(src++);
169				FB_WRITEL(d0 >> right | d1 << left, dst++);
170				d0 = d1;
171				d1 = FB_READL(src++);
172				FB_WRITEL(d0 >> right | d1 << left, dst++);
173				d0 = d1;
174				d1 = FB_READL(src++);
175				FB_WRITEL(d0 >> right | d1 << left, dst++);
176				d0 = d1;
177				n -= 4;
178			}
179			while (n--) {
180				d1 = FB_READL(src++);
181				d1 = fb_rev_pixels_in_long(d1, bswapmask);
182				d0 = d0 >> right | d1 << left;
183				d0 = fb_rev_pixels_in_long(d0, bswapmask);
184				FB_WRITEL(d0, dst++);
185				d0 = d1;
186			}
187
188			// Trailing bits
189			if (m) {
190				if (m <= bits - right) {
191					// Single source word
192					d0 >>= right;
193				} else {
194					// 2 source words
195					d1 = FB_READL(src);
196					d1 = fb_rev_pixels_in_long(d1,
197								bswapmask);
198					d0 = d0 >> right | d1 << left;
199				}
200				d0 = fb_rev_pixels_in_long(d0, bswapmask);
201				FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
202			}
203		}
204	}
205}
206
207    /*
208     *  Generic bitwise copy algorithm, operating backward
209     */
210
211static void
212bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
213		const unsigned long __iomem *src, unsigned src_idx, int bits,
214		unsigned n, u32 bswapmask)
215{
216	unsigned long first, last;
217	int shift;
218
219#if 0
220	/*
221	 * If you suspect bug in this function, compare it with this simple
222	 * memmove implementation.
223	 */
224	memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
225		(char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
226	return;
227#endif
228
229	dst += (dst_idx + n - 1) / bits;
230	src += (src_idx + n - 1) / bits;
231	dst_idx = (dst_idx + n - 1) % bits;
232	src_idx = (src_idx + n - 1) % bits;
233
234	shift = dst_idx-src_idx;
235
236	first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask);
237	last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask);
238
239	if (!shift) {
240		// Same alignment for source and dest
241
242		if ((unsigned long)dst_idx+1 >= n) {
243			// Single word
244			if (first)
245				last &= first;
246			FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
247		} else {
248			// Multiple destination words
249
250			// Leading bits
251			if (first) {
252				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
253				dst--;
254				src--;
255				n -= dst_idx+1;
256			}
257
258			// Main chunk
259			n /= bits;
260			while (n >= 8) {
261				FB_WRITEL(FB_READL(src--), dst--);
262				FB_WRITEL(FB_READL(src--), dst--);
263				FB_WRITEL(FB_READL(src--), dst--);
264				FB_WRITEL(FB_READL(src--), dst--);
265				FB_WRITEL(FB_READL(src--), dst--);
266				FB_WRITEL(FB_READL(src--), dst--);
267				FB_WRITEL(FB_READL(src--), dst--);
268				FB_WRITEL(FB_READL(src--), dst--);
269				n -= 8;
270			}
271			while (n--)
272				FB_WRITEL(FB_READL(src--), dst--);
273
274			// Trailing bits
275			if (last != -1UL)
276				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
277		}
278	} else {
279		// Different alignment for source and dest
280		unsigned long d0, d1;
281		int m;
282
283		int const left = shift & (bits-1);
284		int const right = -shift & (bits-1);
285
286		if ((unsigned long)dst_idx+1 >= n) {
287			// Single destination word
288			if (first)
289				last &= first;
290			d0 = FB_READL(src);
291			if (shift < 0) {
292				// Single source word
293				d0 >>= right;
294			} else if (1+(unsigned long)src_idx >= n) {
295				// Single source word
296				d0 <<= left;
297			} else {
298				// 2 source words
299				d1 = FB_READL(src - 1);
300				d1 = fb_rev_pixels_in_long(d1, bswapmask);
301				d0 = d0 << left | d1 >> right;
302			}
303			d0 = fb_rev_pixels_in_long(d0, bswapmask);
304			FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
305		} else {
306			// Multiple destination words
307			/** We must always remember the last value read, because in case
308			SRC and DST overlap bitwise (e.g. when moving just one pixel in
309			1bpp), we always collect one full long for DST and that might
310			overlap with the current long from SRC. We store this value in
311			'd0'. */
312
313			d0 = FB_READL(src--);
314			d0 = fb_rev_pixels_in_long(d0, bswapmask);
315			// Leading bits
316			if (shift < 0) {
317				// Single source word
318				d1 = d0;
319				d0 >>= right;
320			} else {
321				// 2 source words
322				d1 = FB_READL(src--);
323				d1 = fb_rev_pixels_in_long(d1, bswapmask);
324				d0 = d0 << left | d1 >> right;
325			}
326			d0 = fb_rev_pixels_in_long(d0, bswapmask);
327			if (!first)
328				FB_WRITEL(d0, dst);
329			else
330				FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
331			d0 = d1;
332			dst--;
333			n -= dst_idx+1;
334
335			// Main chunk
336			m = n % bits;
337			n /= bits;
338			while ((n >= 4) && !bswapmask) {
339				d1 = FB_READL(src--);
340				FB_WRITEL(d0 << left | d1 >> right, dst--);
341				d0 = d1;
342				d1 = FB_READL(src--);
343				FB_WRITEL(d0 << left | d1 >> right, dst--);
344				d0 = d1;
345				d1 = FB_READL(src--);
346				FB_WRITEL(d0 << left | d1 >> right, dst--);
347				d0 = d1;
348				d1 = FB_READL(src--);
349				FB_WRITEL(d0 << left | d1 >> right, dst--);
350				d0 = d1;
351				n -= 4;
352			}
353			while (n--) {
354				d1 = FB_READL(src--);
355				d1 = fb_rev_pixels_in_long(d1, bswapmask);
356				d0 = d0 << left | d1 >> right;
357				d0 = fb_rev_pixels_in_long(d0, bswapmask);
358				FB_WRITEL(d0, dst--);
359				d0 = d1;
360			}
361
362			// Trailing bits
363			if (m) {
364				if (m <= bits - left) {
365					// Single source word
366					d0 <<= left;
367				} else {
368					// 2 source words
369					d1 = FB_READL(src);
370					d1 = fb_rev_pixels_in_long(d1,
371								bswapmask);
372					d0 = d0 << left | d1 >> right;
373				}
374				d0 = fb_rev_pixels_in_long(d0, bswapmask);
375				FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
376			}
377		}
378	}
379}
380
381void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
382{
383	u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
384	u32 height = area->height, width = area->width;
385	unsigned int const bits_per_line = p->fix.line_length * 8u;
386	unsigned long __iomem *base = NULL;
387	int bits = BITS_PER_LONG, bytes = bits >> 3;
388	unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
389	u32 bswapmask = fb_compute_bswapmask(p);
390
391	if (p->state != FBINFO_STATE_RUNNING)
392		return;
393
394	if (p->flags & FBINFO_VIRTFB)
395		fb_warn_once(p, "Framebuffer is not in I/O address space.");
396
397	/* if the beginning of the target area might overlap with the end of
398	the source area, be have to copy the area reverse. */
399	if ((dy == sy && dx > sx) || (dy > sy)) {
400		dy += height;
401		sy += height;
402		rev_copy = 1;
403	}
404
405	// split the base of the framebuffer into a long-aligned address and the
406	// index of the first bit
407	base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
408	dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
409	// add offset of source and target area
410	dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
411	src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
412
413	if (p->fbops->fb_sync)
414		p->fbops->fb_sync(p);
415
416	if (rev_copy) {
417		while (height--) {
418			dst_idx -= bits_per_line;
419			src_idx -= bits_per_line;
420			bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
421				base + (src_idx / bits), src_idx % bits, bits,
422				width*p->var.bits_per_pixel, bswapmask);
423		}
424	} else {
425		while (height--) {
426			bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
427				base + (src_idx / bits), src_idx % bits, bits,
428				width*p->var.bits_per_pixel, bswapmask);
429			dst_idx += bits_per_line;
430			src_idx += bits_per_line;
431		}
432	}
433}
434
435EXPORT_SYMBOL(cfb_copyarea);
436
437MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
438MODULE_DESCRIPTION("Generic software accelerated copyarea");
439MODULE_LICENSE("GPL");
440
441