• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
bitcpy(struct fb_info * p,unsigned long __iomem * dst,int dst_idx,const unsigned long __iomem * src,int src_idx,int bits,unsigned n,u32 bswapmask)46 bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
47 		const unsigned long __iomem *src, int src_idx, int bits,
48 		unsigned n, u32 bswapmask)
49 {
50 	unsigned long first, last;
51 	int const shift = dst_idx-src_idx;
52 	int left, right;
53 
54 	first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
55 	last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
56 
57 	if (!shift) {
58 		// Same alignment for source and dest
59 
60 		if (dst_idx+n <= bits) {
61 			// Single word
62 			if (last)
63 				first &= last;
64 			FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
65 		} else {
66 			// Multiple destination words
67 
68 			// Leading bits
69 			if (first != ~0UL) {
70 				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
71 				dst++;
72 				src++;
73 				n -= bits - dst_idx;
74 			}
75 
76 			// Main chunk
77 			n /= bits;
78 			while (n >= 8) {
79 				FB_WRITEL(FB_READL(src++), dst++);
80 				FB_WRITEL(FB_READL(src++), dst++);
81 				FB_WRITEL(FB_READL(src++), dst++);
82 				FB_WRITEL(FB_READL(src++), dst++);
83 				FB_WRITEL(FB_READL(src++), dst++);
84 				FB_WRITEL(FB_READL(src++), dst++);
85 				FB_WRITEL(FB_READL(src++), dst++);
86 				FB_WRITEL(FB_READL(src++), dst++);
87 				n -= 8;
88 			}
89 			while (n--)
90 				FB_WRITEL(FB_READL(src++), dst++);
91 
92 			// Trailing bits
93 			if (last)
94 				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
95 		}
96 	} else {
97 		/* Different alignment for source and dest */
98 		unsigned long d0, d1;
99 		int m;
100 
101 		right = shift & (bits - 1);
102 		left = -shift & (bits - 1);
103 		bswapmask &= shift;
104 
105 		if (dst_idx+n <= bits) {
106 			// Single destination word
107 			if (last)
108 				first &= last;
109 			d0 = FB_READL(src);
110 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
111 			if (shift > 0) {
112 				// Single source word
113 				d0 >>= right;
114 			} else if (src_idx+n <= bits) {
115 				// Single source word
116 				d0 <<= left;
117 			} else {
118 				// 2 source words
119 				d1 = FB_READL(src + 1);
120 				d1 = fb_rev_pixels_in_long(d1, bswapmask);
121 				d0 = d0<<left | d1>>right;
122 			}
123 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
124 			FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
125 		} else {
126 			// Multiple destination words
127 			/** We must always remember the last value read, because in case
128 			SRC and DST overlap bitwise (e.g. when moving just one pixel in
129 			1bpp), we always collect one full long for DST and that might
130 			overlap with the current long from SRC. We store this value in
131 			'd0'. */
132 			d0 = FB_READL(src++);
133 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
134 			// Leading bits
135 			if (shift > 0) {
136 				// Single source word
137 				d1 = d0;
138 				d0 >>= right;
139 				dst++;
140 				n -= bits - dst_idx;
141 			} else {
142 				// 2 source words
143 				d1 = FB_READL(src++);
144 				d1 = fb_rev_pixels_in_long(d1, bswapmask);
145 
146 				d0 = d0<<left | d1>>right;
147 				dst++;
148 				n -= bits - dst_idx;
149 			}
150 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
151 			FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
152 			d0 = d1;
153 
154 			// Main chunk
155 			m = n % bits;
156 			n /= bits;
157 			while ((n >= 4) && !bswapmask) {
158 				d1 = FB_READL(src++);
159 				FB_WRITEL(d0 << left | d1 >> right, dst++);
160 				d0 = d1;
161 				d1 = FB_READL(src++);
162 				FB_WRITEL(d0 << left | d1 >> right, dst++);
163 				d0 = d1;
164 				d1 = FB_READL(src++);
165 				FB_WRITEL(d0 << left | d1 >> right, dst++);
166 				d0 = d1;
167 				d1 = FB_READL(src++);
168 				FB_WRITEL(d0 << left | d1 >> right, dst++);
169 				d0 = d1;
170 				n -= 4;
171 			}
172 			while (n--) {
173 				d1 = FB_READL(src++);
174 				d1 = fb_rev_pixels_in_long(d1, bswapmask);
175 				d0 = d0 << left | d1 >> right;
176 				d0 = fb_rev_pixels_in_long(d0, bswapmask);
177 				FB_WRITEL(d0, dst++);
178 				d0 = d1;
179 			}
180 
181 			// Trailing bits
182 			if (last) {
183 				if (m <= right) {
184 					// Single source word
185 					d0 <<= left;
186 				} else {
187 					// 2 source words
188 					d1 = FB_READL(src);
189 					d1 = fb_rev_pixels_in_long(d1,
190 								bswapmask);
191 					d0 = d0<<left | d1>>right;
192 				}
193 				d0 = fb_rev_pixels_in_long(d0, bswapmask);
194 				FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
195 			}
196 		}
197 	}
198 }
199 
200     /*
201      *  Generic bitwise copy algorithm, operating backward
202      */
203 
204 static void
bitcpy_rev(struct fb_info * p,unsigned long __iomem * dst,int dst_idx,const unsigned long __iomem * src,int src_idx,int bits,unsigned n,u32 bswapmask)205 bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
206 		const unsigned long __iomem *src, int src_idx, int bits,
207 		unsigned n, u32 bswapmask)
208 {
209 	unsigned long first, last;
210 	int shift;
211 
212 	dst += (n-1)/bits;
213 	src += (n-1)/bits;
214 	if ((n-1) % bits) {
215 		dst_idx += (n-1) % bits;
216 		dst += dst_idx >> (ffs(bits) - 1);
217 		dst_idx &= bits - 1;
218 		src_idx += (n-1) % bits;
219 		src += src_idx >> (ffs(bits) - 1);
220 		src_idx &= bits - 1;
221 	}
222 
223 	shift = dst_idx-src_idx;
224 
225 	first = fb_shifted_pixels_mask_long(p, bits - 1 - dst_idx, bswapmask);
226 	last = ~fb_shifted_pixels_mask_long(p, bits - 1 - ((dst_idx-n) % bits),
227 					    bswapmask);
228 
229 	if (!shift) {
230 		// Same alignment for source and dest
231 
232 		if ((unsigned long)dst_idx+1 >= n) {
233 			// Single word
234 			if (last)
235 				first &= last;
236 			FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
237 		} else {
238 			// Multiple destination words
239 
240 			// Leading bits
241 			if (first != ~0UL) {
242 				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
243 				dst--;
244 				src--;
245 				n -= dst_idx+1;
246 			}
247 
248 			// Main chunk
249 			n /= bits;
250 			while (n >= 8) {
251 				FB_WRITEL(FB_READL(src--), dst--);
252 				FB_WRITEL(FB_READL(src--), dst--);
253 				FB_WRITEL(FB_READL(src--), dst--);
254 				FB_WRITEL(FB_READL(src--), dst--);
255 				FB_WRITEL(FB_READL(src--), dst--);
256 				FB_WRITEL(FB_READL(src--), dst--);
257 				FB_WRITEL(FB_READL(src--), dst--);
258 				FB_WRITEL(FB_READL(src--), dst--);
259 				n -= 8;
260 			}
261 			while (n--)
262 				FB_WRITEL(FB_READL(src--), dst--);
263 
264 			// Trailing bits
265 			if (last)
266 				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
267 		}
268 	} else {
269 		// Different alignment for source and dest
270 		unsigned long d0, d1;
271 		int m;
272 
273 		int const left = -shift & (bits-1);
274 		int const right = shift & (bits-1);
275 		bswapmask &= shift;
276 
277 		if ((unsigned long)dst_idx+1 >= n) {
278 			// Single destination word
279 			if (last)
280 				first &= last;
281 			d0 = FB_READL(src);
282 			if (shift < 0) {
283 				// Single source word
284 				d0 <<= left;
285 			} else if (1+(unsigned long)src_idx >= n) {
286 				// Single source word
287 				d0 >>= right;
288 			} else {
289 				// 2 source words
290 				d1 = FB_READL(src - 1);
291 				d1 = fb_rev_pixels_in_long(d1, bswapmask);
292 				d0 = d0>>right | d1<<left;
293 			}
294 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
295 			FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
296 		} else {
297 			// Multiple destination words
298 			/** We must always remember the last value read, because in case
299 			SRC and DST overlap bitwise (e.g. when moving just one pixel in
300 			1bpp), we always collect one full long for DST and that might
301 			overlap with the current long from SRC. We store this value in
302 			'd0'. */
303 
304 			d0 = FB_READL(src--);
305 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
306 			// Leading bits
307 			if (shift < 0) {
308 				// Single source word
309 				d1 = d0;
310 				d0 <<= left;
311 			} else {
312 				// 2 source words
313 				d1 = FB_READL(src--);
314 				d1 = fb_rev_pixels_in_long(d1, bswapmask);
315 				d0 = d0>>right | d1<<left;
316 			}
317 			d0 = fb_rev_pixels_in_long(d0, bswapmask);
318 			FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
319 			d0 = d1;
320 			dst--;
321 			n -= dst_idx+1;
322 
323 			// Main chunk
324 			m = n % bits;
325 			n /= bits;
326 			while ((n >= 4) && !bswapmask) {
327 				d1 = FB_READL(src--);
328 				FB_WRITEL(d0 >> right | d1 << left, dst--);
329 				d0 = d1;
330 				d1 = FB_READL(src--);
331 				FB_WRITEL(d0 >> right | d1 << left, dst--);
332 				d0 = d1;
333 				d1 = FB_READL(src--);
334 				FB_WRITEL(d0 >> right | d1 << left, dst--);
335 				d0 = d1;
336 				d1 = FB_READL(src--);
337 				FB_WRITEL(d0 >> right | d1 << left, dst--);
338 				d0 = d1;
339 				n -= 4;
340 			}
341 			while (n--) {
342 				d1 = FB_READL(src--);
343 				d1 = fb_rev_pixels_in_long(d1, bswapmask);
344 				d0 = d0 >> right | d1 << left;
345 				d0 = fb_rev_pixels_in_long(d0, bswapmask);
346 				FB_WRITEL(d0, dst--);
347 				d0 = d1;
348 			}
349 
350 			// Trailing bits
351 			if (last) {
352 				if (m <= left) {
353 					// Single source word
354 					d0 >>= right;
355 				} else {
356 					// 2 source words
357 					d1 = FB_READL(src);
358 					d1 = fb_rev_pixels_in_long(d1,
359 								bswapmask);
360 					d0 = d0>>right | d1<<left;
361 				}
362 				d0 = fb_rev_pixels_in_long(d0, bswapmask);
363 				FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
364 			}
365 		}
366 	}
367 }
368 
cfb_copyarea(struct fb_info * p,const struct fb_copyarea * area)369 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
370 {
371 	u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
372 	u32 height = area->height, width = area->width;
373 	unsigned long const bits_per_line = p->fix.line_length*8u;
374 	unsigned long __iomem *dst = NULL, *src = NULL;
375 	int bits = BITS_PER_LONG, bytes = bits >> 3;
376 	int dst_idx = 0, src_idx = 0, rev_copy = 0;
377 	u32 bswapmask = fb_compute_bswapmask(p);
378 
379 	if (p->state != FBINFO_STATE_RUNNING)
380 		return;
381 
382 	/* if the beginning of the target area might overlap with the end of
383 	the source area, be have to copy the area reverse. */
384 	if ((dy == sy && dx > sx) || (dy > sy)) {
385 		dy += height;
386 		sy += height;
387 		rev_copy = 1;
388 	}
389 
390 	// split the base of the framebuffer into a long-aligned address and the
391 	// index of the first bit
392 	dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
393 	dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
394 	// add offset of source and target area
395 	dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
396 	src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
397 
398 	if (p->fbops->fb_sync)
399 		p->fbops->fb_sync(p);
400 
401 	if (rev_copy) {
402 		while (height--) {
403 			dst_idx -= bits_per_line;
404 			src_idx -= bits_per_line;
405 			dst += dst_idx >> (ffs(bits) - 1);
406 			dst_idx &= (bytes - 1);
407 			src += src_idx >> (ffs(bits) - 1);
408 			src_idx &= (bytes - 1);
409 			bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
410 				width*p->var.bits_per_pixel, bswapmask);
411 		}
412 	} else {
413 		while (height--) {
414 			dst += dst_idx >> (ffs(bits) - 1);
415 			dst_idx &= (bytes - 1);
416 			src += src_idx >> (ffs(bits) - 1);
417 			src_idx &= (bytes - 1);
418 			bitcpy(p, dst, dst_idx, src, src_idx, bits,
419 				width*p->var.bits_per_pixel, bswapmask);
420 			dst_idx += bits_per_line;
421 			src_idx += bits_per_line;
422 		}
423 	}
424 }
425 
426 EXPORT_SYMBOL(cfb_copyarea);
427 
428 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
429 MODULE_DESCRIPTION("Generic software accelerated copyarea");
430 MODULE_LICENSE("GPL");
431 
432