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