blob: b07e419b12d289f79dfeb2a34f1e87c66b12c971 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#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>
Antonino A. Daplasdc0e6e02007-05-08 00:39:08 -070032#include "fb_draw.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
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 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 * Generic bitwise copy algorithm
44 */
45
46static void
47bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
Pavel Pisa779121e2007-10-16 01:29:21 -070048 int src_idx, int bits, unsigned n, u32 bswapmask)
Linus Torvalds1da177e2005-04-16 15:20:36 -070049{
50 unsigned long first, last;
51 int const shift = dst_idx-src_idx;
52 int left, right;
53
Pavel Pisa779121e2007-10-16 01:29:21 -070054 first = fb_shifted_pixels_mask_long(dst_idx, bswapmask);
55 last = ~fb_shifted_pixels_mask_long((dst_idx+n) % bits, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
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 {
Pavel Pisa15afdd42007-10-16 01:29:55 -070097 /* Different alignment for source and dest */
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 unsigned long d0, d1;
99 int m;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
101 right = shift & (bits - 1);
102 left = -shift & (bits - 1);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700103 bswapmask &= shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104
105 if (dst_idx+n <= bits) {
106 // Single destination word
107 if (last)
108 first &= last;
Pavel Pisa15afdd42007-10-16 01:29:55 -0700109 d0 = FB_READL(src);
110 d0 = fb_rev_pixels_in_long(d0, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 if (shift > 0) {
112 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700113 d0 >>= right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 } else if (src_idx+n <= bits) {
115 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700116 d0 <<= left;;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 } else {
118 // 2 source words
Pavel Pisa15afdd42007-10-16 01:29:55 -0700119 d1 = FB_READL(src + 1);
120 d1 = fb_rev_pixels_in_long(d1, bswapmask);
121 d0 = d0<<left | d1>>right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700123 d0 = fb_rev_pixels_in_long(d0, bswapmask);
124 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 } 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++);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700133 d0 = fb_rev_pixels_in_long(d0, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 // Leading bits
135 if (shift > 0) {
136 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700137 d1 = d0;
138 d0 >>= right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 dst++;
140 n -= bits - dst_idx;
141 } else {
142 // 2 source words
143 d1 = FB_READL(src++);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700144 d1 = fb_rev_pixels_in_long(d1, bswapmask);
145
146 d0 = d0<<left | d1>>right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 dst++;
148 n -= bits - dst_idx;
149 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700150 d0 = fb_rev_pixels_in_long(d0, bswapmask);
151 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
152 d0 = d1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
154 // Main chunk
155 m = n % bits;
156 n /= bits;
Pavel Pisa15afdd42007-10-16 01:29:55 -0700157 while ((n >= 4) && !bswapmask) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 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++);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700174 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++);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 d0 = d1;
179 }
180
181 // Trailing bits
182 if (last) {
183 if (m <= right) {
184 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700185 d0 <<= left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 } else {
187 // 2 source words
188 d1 = FB_READL(src);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700189 d1 = fb_rev_pixels_in_long(d1,
190 bswapmask);
191 d0 = d0<<left | d1>>right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700193 d0 = fb_rev_pixels_in_long(d0, bswapmask);
194 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 }
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,
Pavel Pisa779121e2007-10-16 01:29:21 -0700206 int src_idx, int bits, unsigned n, u32 bswapmask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207{
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
Pavel Pisa779121e2007-10-16 01:29:21 -0700224 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226
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
Pavel Pisa15afdd42007-10-16 01:29:55 -0700268 unsigned long d0, d1;
269 int m;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270
271 int const left = -shift & (bits-1);
272 int const right = shift & (bits-1);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700273 bswapmask &= shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274
275 if ((unsigned long)dst_idx+1 >= n) {
276 // Single destination word
277 if (last)
278 first &= last;
Pavel Pisa15afdd42007-10-16 01:29:55 -0700279 d0 = FB_READL(src);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 if (shift < 0) {
281 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700282 d0 <<= left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 } else if (1+(unsigned long)src_idx >= n) {
284 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700285 d0 >>= right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 } else {
287 // 2 source words
Pavel Pisa15afdd42007-10-16 01:29:55 -0700288 d1 = FB_READL(src - 1);
289 d1 = fb_rev_pixels_in_long(d1, bswapmask);
290 d0 = d0>>right | d1<<left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700292 d0 = fb_rev_pixels_in_long(d0, bswapmask);
293 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 } 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'. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
302 d0 = FB_READL(src--);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700303 d0 = fb_rev_pixels_in_long(d0, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 // Leading bits
305 if (shift < 0) {
306 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700307 d1 = d0;
308 d0 <<= left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 } else {
310 // 2 source words
311 d1 = FB_READL(src--);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700312 d1 = fb_rev_pixels_in_long(d1, bswapmask);
313 d0 = d0>>right | d1<<left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700315 d0 = fb_rev_pixels_in_long(d0, bswapmask);
316 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
317 d0 = d1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 dst--;
319 n -= dst_idx+1;
320
321 // Main chunk
322 m = n % bits;
323 n /= bits;
Pavel Pisa15afdd42007-10-16 01:29:55 -0700324 while ((n >= 4) && !bswapmask) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 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--);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700341 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--);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 d0 = d1;
346 }
347
348 // Trailing bits
349 if (last) {
350 if (m <= left) {
351 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700352 d0 >>= right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 } else {
354 // 2 source words
355 d1 = FB_READL(src);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700356 d1 = fb_rev_pixels_in_long(d1,
357 bswapmask);
358 d0 = d0>>right | d1<<left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700360 d0 = fb_rev_pixels_in_long(d0, bswapmask);
361 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 }
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;
Pavel Pisa779121e2007-10-16 01:29:21 -0700375 u32 bswapmask = fb_compute_bswapmask(p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
377 if (p->state != FBINFO_STATE_RUNNING)
378 return;
379
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 /* 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,
Pavel Pisa779121e2007-10-16 01:29:21 -0700408 width*p->var.bits_per_pixel, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 }
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,
Pavel Pisa779121e2007-10-16 01:29:21 -0700417 width*p->var.bits_per_pixel, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 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