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