Blame view

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