Linux 2.6.26-rc4
[linux-2.6/openmoko-kernel/knife-kernel.git] / drivers / video / cfbcopyarea.c
blobdf03f3776dcc3b41503018c0cc7d8045bbe67d88
1 /*
2 * Generic function for frame buffer with packed pixels of any depth.
4 * Copyright (C) 1999-2005 James Simmons <jsimmons@www.infradead.org>
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.
10 * NOTES:
12 * This is for cfb packed pixels. Iplan and such are incorporated in the
13 * drivers that need them.
15 * FIXME
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.
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.
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"
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
43 * Generic bitwise copy algorithm
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)
51 unsigned long first, last;
52 int const shift = dst_idx-src_idx;
53 int left, right;
55 first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
56 last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
58 if (!shift) {
59 // Same alignment for source and dest
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
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;
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;
90 while (n--)
91 FB_WRITEL(FB_READL(src++), dst++);
93 // Trailing bits
94 if (last)
95 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
97 } else {
98 /* Different alignment for source and dest */
99 unsigned long d0, d1;
100 int m;
102 right = shift & (bits - 1);
103 left = -shift & (bits - 1);
104 bswapmask &= shift;
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;
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);
147 d0 = d0<<left | d1>>right;
148 dst++;
149 n -= bits - dst_idx;
151 d0 = fb_rev_pixels_in_long(d0, bswapmask);
152 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
153 d0 = d1;
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;
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;
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;
194 d0 = fb_rev_pixels_in_long(d0, bswapmask);
195 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
202 * Generic bitwise copy algorithm, operating backward
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)
210 unsigned long first, last;
211 int shift;
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;
224 shift = dst_idx-src_idx;
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);
230 if (!shift) {
231 // Same alignment for source and dest
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
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;
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;
262 while (n--)
263 FB_WRITEL(FB_READL(src--), dst--);
265 // Trailing bits
266 if (last)
267 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
269 } else {
270 // Different alignment for source and dest
271 unsigned long d0, d1;
272 int m;
274 int const left = -shift & (bits-1);
275 int const right = shift & (bits-1);
276 bswapmask &= shift;
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;
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'. */
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;
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;
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;
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;
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;
363 d0 = fb_rev_pixels_in_long(d0, bswapmask);
364 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
370 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
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);
380 if (p->state != FBINFO_STATE_RUNNING)
381 return;
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;
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;
399 if (p->fbops->fb_sync)
400 p->fbops->fb_sync(p);
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);
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;
427 EXPORT_SYMBOL(cfb_copyarea);
429 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
430 MODULE_DESCRIPTION("Generic software accelerated copyarea");
431 MODULE_LICENSE("GPL");