xref: /linux/drivers/video/fbdev/core/cfbcopyarea.c (revision fd7d598270724cc787982ea48bbe17ad383a8b7f)
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 
45 static void
46 bitcpy(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 
211 static void
212 bitcpy_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 
381 void 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 the beginning of the target area might overlap with the end of
395 	the source area, be have to copy the area reverse. */
396 	if ((dy == sy && dx > sx) || (dy > sy)) {
397 		dy += height;
398 		sy += height;
399 		rev_copy = 1;
400 	}
401 
402 	// split the base of the framebuffer into a long-aligned address and the
403 	// index of the first bit
404 	base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
405 	dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
406 	// add offset of source and target area
407 	dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
408 	src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
409 
410 	if (p->fbops->fb_sync)
411 		p->fbops->fb_sync(p);
412 
413 	if (rev_copy) {
414 		while (height--) {
415 			dst_idx -= bits_per_line;
416 			src_idx -= bits_per_line;
417 			bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
418 				base + (src_idx / bits), src_idx % bits, bits,
419 				width*p->var.bits_per_pixel, bswapmask);
420 		}
421 	} else {
422 		while (height--) {
423 			bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
424 				base + (src_idx / bits), src_idx % bits, bits,
425 				width*p->var.bits_per_pixel, bswapmask);
426 			dst_idx += bits_per_line;
427 			src_idx += bits_per_line;
428 		}
429 	}
430 }
431 
432 EXPORT_SYMBOL(cfb_copyarea);
433 
434 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
435 MODULE_DESCRIPTION("Generic software accelerated copyarea");
436 MODULE_LICENSE("GPL");
437 
438