blob: df03f3776dcc3b41503018c0cc7d8045bbe67d88 [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
Anton Vorontsove4c690e2008-04-28 02:14:49 -070047bitcpy(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)
Linus Torvalds1da177e2005-04-16 15:20:36 -070050{
51 unsigned long first, last;
52 int const shift = dst_idx-src_idx;
53 int left, right;
54
Anton Vorontsove4c690e2008-04-28 02:14:49 -070055 first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
56 last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
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 {
Pavel Pisa15afdd42007-10-16 01:29:55 -070098 /* Different alignment for source and dest */
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 unsigned long d0, d1;
100 int m;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
102 right = shift & (bits - 1);
103 left = -shift & (bits - 1);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700104 bswapmask &= shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105
106 if (dst_idx+n <= bits) {
107 // Single destination word
108 if (last)
109 first &= last;
Pavel Pisa15afdd42007-10-16 01:29:55 -0700110 d0 = FB_READL(src);
111 d0 = fb_rev_pixels_in_long(d0, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 if (shift > 0) {
113 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700114 d0 >>= right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 } else if (src_idx+n <= bits) {
116 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700117 d0 <<= left;;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 } else {
119 // 2 source words
Pavel Pisa15afdd42007-10-16 01:29:55 -0700120 d1 = FB_READL(src + 1);
121 d1 = fb_rev_pixels_in_long(d1, bswapmask);
122 d0 = d0<<left | d1>>right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700124 d0 = fb_rev_pixels_in_long(d0, bswapmask);
125 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 } 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++);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700134 d0 = fb_rev_pixels_in_long(d0, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 // Leading bits
136 if (shift > 0) {
137 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700138 d1 = d0;
139 d0 >>= right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 dst++;
141 n -= bits - dst_idx;
142 } else {
143 // 2 source words
144 d1 = FB_READL(src++);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700145 d1 = fb_rev_pixels_in_long(d1, bswapmask);
146
147 d0 = d0<<left | d1>>right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 dst++;
149 n -= bits - dst_idx;
150 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700151 d0 = fb_rev_pixels_in_long(d0, bswapmask);
152 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
153 d0 = d1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
155 // Main chunk
156 m = n % bits;
157 n /= bits;
Pavel Pisa15afdd42007-10-16 01:29:55 -0700158 while ((n >= 4) && !bswapmask) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 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++);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700175 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++);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 d0 = d1;
180 }
181
182 // Trailing bits
183 if (last) {
184 if (m <= right) {
185 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700186 d0 <<= left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 } else {
188 // 2 source words
189 d1 = FB_READL(src);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700190 d1 = fb_rev_pixels_in_long(d1,
191 bswapmask);
192 d0 = d0<<left | d1>>right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700194 d0 = fb_rev_pixels_in_long(d0, bswapmask);
195 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 }
197 }
198 }
199}
200
201 /*
202 * Generic bitwise copy algorithm, operating backward
203 */
204
205static void
Anton Vorontsove4c690e2008-04-28 02:14:49 -0700206bitcpy_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)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209{
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
Anton Vorontsove4c690e2008-04-28 02:14:49 -0700226 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229
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
Pavel Pisa15afdd42007-10-16 01:29:55 -0700271 unsigned long d0, d1;
272 int m;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273
274 int const left = -shift & (bits-1);
275 int const right = shift & (bits-1);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700276 bswapmask &= shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
278 if ((unsigned long)dst_idx+1 >= n) {
279 // Single destination word
280 if (last)
281 first &= last;
Pavel Pisa15afdd42007-10-16 01:29:55 -0700282 d0 = FB_READL(src);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 if (shift < 0) {
284 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700285 d0 <<= left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 } else if (1+(unsigned long)src_idx >= n) {
287 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700288 d0 >>= right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 } else {
290 // 2 source words
Pavel Pisa15afdd42007-10-16 01:29:55 -0700291 d1 = FB_READL(src - 1);
292 d1 = fb_rev_pixels_in_long(d1, bswapmask);
293 d0 = d0>>right | d1<<left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700295 d0 = fb_rev_pixels_in_long(d0, bswapmask);
296 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 } 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'. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
305 d0 = FB_READL(src--);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700306 d0 = fb_rev_pixels_in_long(d0, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 // Leading bits
308 if (shift < 0) {
309 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700310 d1 = d0;
311 d0 <<= left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 } else {
313 // 2 source words
314 d1 = FB_READL(src--);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700315 d1 = fb_rev_pixels_in_long(d1, bswapmask);
316 d0 = d0>>right | d1<<left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700318 d0 = fb_rev_pixels_in_long(d0, bswapmask);
319 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
320 d0 = d1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 dst--;
322 n -= dst_idx+1;
323
324 // Main chunk
325 m = n % bits;
326 n /= bits;
Pavel Pisa15afdd42007-10-16 01:29:55 -0700327 while ((n >= 4) && !bswapmask) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 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--);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700344 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--);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 d0 = d1;
349 }
350
351 // Trailing bits
352 if (last) {
353 if (m <= left) {
354 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700355 d0 >>= right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 } else {
357 // 2 source words
358 d1 = FB_READL(src);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700359 d1 = fb_rev_pixels_in_long(d1,
360 bswapmask);
361 d0 = d0>>right | d1<<left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700363 d0 = fb_rev_pixels_in_long(d0, bswapmask);
364 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 }
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;
Pavel Pisa779121e2007-10-16 01:29:21 -0700378 u32 bswapmask = fb_compute_bswapmask(p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
380 if (p->state != FBINFO_STATE_RUNNING)
381 return;
382
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 /* 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);
Anton Vorontsove4c690e2008-04-28 02:14:49 -0700410 bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
Pavel Pisa779121e2007-10-16 01:29:21 -0700411 width*p->var.bits_per_pixel, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 }
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);
Anton Vorontsove4c690e2008-04-28 02:14:49 -0700419 bitcpy(p, dst, dst_idx, src, src_idx, bits,
Pavel Pisa779121e2007-10-16 01:29:21 -0700420 width*p->var.bits_per_pixel, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 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