SPI: spi_txx9: Fix bit rate calculation
[linux-2.6-block.git] / drivers / video / cfbcopyarea.c
CommitLineData
1da177e4
LT
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
1da177e4
LT
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>
dc0e6e05 32#include "fb_draw.h"
1da177e4
LT
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
1da177e4
LT
42 /*
43 * Generic bitwise copy algorithm
44 */
45
46static void
e4c690e0
AV
47bitcpy(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)
1da177e4
LT
50{
51 unsigned long first, last;
52 int const shift = dst_idx-src_idx;
53 int left, right;
54
e4c690e0
AV
55 first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
56 last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
1da177e4
LT
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 {
15afdd43 98 /* Different alignment for source and dest */
1da177e4
LT
99 unsigned long d0, d1;
100 int m;
1da177e4
LT
101
102 right = shift & (bits - 1);
103 left = -shift & (bits - 1);
15afdd43 104 bswapmask &= shift;
1da177e4
LT
105
106 if (dst_idx+n <= bits) {
107 // Single destination word
108 if (last)
109 first &= last;
15afdd43
PP
110 d0 = FB_READL(src);
111 d0 = fb_rev_pixels_in_long(d0, bswapmask);
1da177e4
LT
112 if (shift > 0) {
113 // Single source word
15afdd43 114 d0 >>= right;
1da177e4
LT
115 } else if (src_idx+n <= bits) {
116 // Single source word
a419aef8 117 d0 <<= left;
1da177e4
LT
118 } else {
119 // 2 source words
15afdd43
PP
120 d1 = FB_READL(src + 1);
121 d1 = fb_rev_pixels_in_long(d1, bswapmask);
122 d0 = d0<<left | d1>>right;
1da177e4 123 }
15afdd43
PP
124 d0 = fb_rev_pixels_in_long(d0, bswapmask);
125 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
1da177e4
LT
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++);
15afdd43 134 d0 = fb_rev_pixels_in_long(d0, bswapmask);
1da177e4
LT
135 // Leading bits
136 if (shift > 0) {
137 // Single source word
15afdd43
PP
138 d1 = d0;
139 d0 >>= right;
1da177e4
LT
140 dst++;
141 n -= bits - dst_idx;
142 } else {
143 // 2 source words
144 d1 = FB_READL(src++);
15afdd43
PP
145 d1 = fb_rev_pixels_in_long(d1, bswapmask);
146
147 d0 = d0<<left | d1>>right;
1da177e4
LT
148 dst++;
149 n -= bits - dst_idx;
150 }
15afdd43
PP
151 d0 = fb_rev_pixels_in_long(d0, bswapmask);
152 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
153 d0 = d1;
1da177e4
LT
154
155 // Main chunk
156 m = n % bits;
157 n /= bits;
15afdd43 158 while ((n >= 4) && !bswapmask) {
1da177e4
LT
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++);
15afdd43
PP
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++);
1da177e4
LT
179 d0 = d1;
180 }
181
182 // Trailing bits
183 if (last) {
184 if (m <= right) {
185 // Single source word
15afdd43 186 d0 <<= left;
1da177e4
LT
187 } else {
188 // 2 source words
189 d1 = FB_READL(src);
15afdd43
PP
190 d1 = fb_rev_pixels_in_long(d1,
191 bswapmask);
192 d0 = d0<<left | d1>>right;
1da177e4 193 }
15afdd43
PP
194 d0 = fb_rev_pixels_in_long(d0, bswapmask);
195 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
1da177e4
LT
196 }
197 }
198 }
199}
200
201 /*
202 * Generic bitwise copy algorithm, operating backward
203 */
204
205static void
e4c690e0
AV
206bitcpy_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)
1da177e4
LT
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
e4c690e0
AV
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);
1da177e4
LT
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
15afdd43
PP
271 unsigned long d0, d1;
272 int m;
1da177e4
LT
273
274 int const left = -shift & (bits-1);
275 int const right = shift & (bits-1);
15afdd43 276 bswapmask &= shift;
1da177e4
LT
277
278 if ((unsigned long)dst_idx+1 >= n) {
279 // Single destination word
280 if (last)
281 first &= last;
15afdd43 282 d0 = FB_READL(src);
1da177e4
LT
283 if (shift < 0) {
284 // Single source word
15afdd43 285 d0 <<= left;
1da177e4
LT
286 } else if (1+(unsigned long)src_idx >= n) {
287 // Single source word
15afdd43 288 d0 >>= right;
1da177e4
LT
289 } else {
290 // 2 source words
15afdd43
PP
291 d1 = FB_READL(src - 1);
292 d1 = fb_rev_pixels_in_long(d1, bswapmask);
293 d0 = d0>>right | d1<<left;
1da177e4 294 }
15afdd43
PP
295 d0 = fb_rev_pixels_in_long(d0, bswapmask);
296 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
1da177e4
LT
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'. */
1da177e4
LT
304
305 d0 = FB_READL(src--);
15afdd43 306 d0 = fb_rev_pixels_in_long(d0, bswapmask);
1da177e4
LT
307 // Leading bits
308 if (shift < 0) {
309 // Single source word
15afdd43
PP
310 d1 = d0;
311 d0 <<= left;
1da177e4
LT
312 } else {
313 // 2 source words
314 d1 = FB_READL(src--);
15afdd43
PP
315 d1 = fb_rev_pixels_in_long(d1, bswapmask);
316 d0 = d0>>right | d1<<left;
1da177e4 317 }
15afdd43
PP
318 d0 = fb_rev_pixels_in_long(d0, bswapmask);
319 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
320 d0 = d1;
1da177e4
LT
321 dst--;
322 n -= dst_idx+1;
323
324 // Main chunk
325 m = n % bits;
326 n /= bits;
15afdd43 327 while ((n >= 4) && !bswapmask) {
1da177e4
LT
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--);
15afdd43
PP
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--);
1da177e4
LT
348 d0 = d1;
349 }
350
351 // Trailing bits
352 if (last) {
353 if (m <= left) {
354 // Single source word
15afdd43 355 d0 >>= right;
1da177e4
LT
356 } else {
357 // 2 source words
358 d1 = FB_READL(src);
15afdd43
PP
359 d1 = fb_rev_pixels_in_long(d1,
360 bswapmask);
361 d0 = d0>>right | d1<<left;
1da177e4 362 }
15afdd43
PP
363 d0 = fb_rev_pixels_in_long(d0, bswapmask);
364 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
1da177e4
LT
365 }
366 }
367 }
368}
369
370void 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;
779121e9 378 u32 bswapmask = fb_compute_bswapmask(p);
1da177e4
LT
379
380 if (p->state != FBINFO_STATE_RUNNING)
381 return;
382
1da177e4
LT
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);
e4c690e0 410 bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
779121e9 411 width*p->var.bits_per_pixel, bswapmask);
1da177e4
LT
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);
e4c690e0 419 bitcpy(p, dst, dst_idx, src, src_idx, bits,
779121e9 420 width*p->var.bits_per_pixel, bswapmask);
1da177e4
LT
421 dst_idx += bits_per_line;
422 src_idx += bits_per_line;
423 }
424 }
425}
426
427EXPORT_SYMBOL(cfb_copyarea);
428
429MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
430MODULE_DESCRIPTION("Generic software accelerated copyarea");
431MODULE_LICENSE("GPL");
432