blob: bb5a96b1645dc4a46600ee38f387e6d4267f3618 [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include <asm/types.h>
30#include <asm/io.h>
Antonino A. Daplasdc0e6e02007-05-08 00:39:08 -070031#include "fb_draw.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
33#if BITS_PER_LONG == 32
34# define FB_WRITEL fb_writel
35# define FB_READL fb_readl
36#else
37# define FB_WRITEL fb_writeq
38# define FB_READL fb_readq
39#endif
40
41 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 * Generic bitwise copy algorithm
43 */
44
45static void
Anton Vorontsove4c690e2008-04-28 02:14:49 -070046bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
47 const unsigned long __iomem *src, int src_idx, int bits,
48 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
Anton Vorontsove4c690e2008-04-28 02:14:49 -070054 first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
55 last = ~fb_shifted_pixels_mask_long(p, (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
Joe Perchesa419aef2009-08-18 11:18:35 -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
Anton Vorontsove4c690e2008-04-28 02:14:49 -0700205bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
206 const unsigned long __iomem *src, int src_idx, int bits,
207 unsigned n, u32 bswapmask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208{
209 unsigned long first, last;
210 int shift;
211
212 dst += (n-1)/bits;
213 src += (n-1)/bits;
214 if ((n-1) % bits) {
215 dst_idx += (n-1) % bits;
216 dst += dst_idx >> (ffs(bits) - 1);
217 dst_idx &= bits - 1;
218 src_idx += (n-1) % bits;
219 src += src_idx >> (ffs(bits) - 1);
220 src_idx &= bits - 1;
221 }
222
223 shift = dst_idx-src_idx;
224
Anton Vorontsove4c690e2008-04-28 02:14:49 -0700225 first = fb_shifted_pixels_mask_long(p, bits - 1 - dst_idx, bswapmask);
226 last = ~fb_shifted_pixels_mask_long(p, bits - 1 - ((dst_idx-n) % bits),
227 bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228
229 if (!shift) {
230 // Same alignment for source and dest
231
232 if ((unsigned long)dst_idx+1 >= n) {
233 // Single word
234 if (last)
235 first &= last;
236 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
237 } else {
238 // Multiple destination words
239
240 // Leading bits
241 if (first != ~0UL) {
242 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
243 dst--;
244 src--;
245 n -= dst_idx+1;
246 }
247
248 // Main chunk
249 n /= bits;
250 while (n >= 8) {
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 FB_WRITEL(FB_READL(src--), dst--);
258 FB_WRITEL(FB_READL(src--), dst--);
259 n -= 8;
260 }
261 while (n--)
262 FB_WRITEL(FB_READL(src--), dst--);
263
264 // Trailing bits
265 if (last)
266 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
267 }
268 } else {
269 // Different alignment for source and dest
Pavel Pisa15afdd42007-10-16 01:29:55 -0700270 unsigned long d0, d1;
271 int m;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272
273 int const left = -shift & (bits-1);
274 int const right = shift & (bits-1);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700275 bswapmask &= shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
277 if ((unsigned long)dst_idx+1 >= n) {
278 // Single destination word
279 if (last)
280 first &= last;
Pavel Pisa15afdd42007-10-16 01:29:55 -0700281 d0 = FB_READL(src);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 if (shift < 0) {
283 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700284 d0 <<= left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 } else if (1+(unsigned long)src_idx >= n) {
286 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700287 d0 >>= right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 } else {
289 // 2 source words
Pavel Pisa15afdd42007-10-16 01:29:55 -0700290 d1 = FB_READL(src - 1);
291 d1 = fb_rev_pixels_in_long(d1, bswapmask);
292 d0 = d0>>right | d1<<left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700294 d0 = fb_rev_pixels_in_long(d0, bswapmask);
295 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 } else {
297 // Multiple destination words
298 /** We must always remember the last value read, because in case
299 SRC and DST overlap bitwise (e.g. when moving just one pixel in
300 1bpp), we always collect one full long for DST and that might
301 overlap with the current long from SRC. We store this value in
302 'd0'. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
304 d0 = FB_READL(src--);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700305 d0 = fb_rev_pixels_in_long(d0, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 // Leading bits
307 if (shift < 0) {
308 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700309 d1 = d0;
310 d0 <<= left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 } else {
312 // 2 source words
313 d1 = FB_READL(src--);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700314 d1 = fb_rev_pixels_in_long(d1, bswapmask);
315 d0 = d0>>right | d1<<left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700317 d0 = fb_rev_pixels_in_long(d0, bswapmask);
318 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
319 d0 = d1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 dst--;
321 n -= dst_idx+1;
322
323 // Main chunk
324 m = n % bits;
325 n /= bits;
Pavel Pisa15afdd42007-10-16 01:29:55 -0700326 while ((n >= 4) && !bswapmask) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 d1 = FB_READL(src--);
328 FB_WRITEL(d0 >> right | d1 << left, dst--);
329 d0 = d1;
330 d1 = FB_READL(src--);
331 FB_WRITEL(d0 >> right | d1 << left, dst--);
332 d0 = d1;
333 d1 = FB_READL(src--);
334 FB_WRITEL(d0 >> right | d1 << left, dst--);
335 d0 = d1;
336 d1 = FB_READL(src--);
337 FB_WRITEL(d0 >> right | d1 << left, dst--);
338 d0 = d1;
339 n -= 4;
340 }
341 while (n--) {
342 d1 = FB_READL(src--);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700343 d1 = fb_rev_pixels_in_long(d1, bswapmask);
344 d0 = d0 >> right | d1 << left;
345 d0 = fb_rev_pixels_in_long(d0, bswapmask);
346 FB_WRITEL(d0, dst--);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 d0 = d1;
348 }
349
350 // Trailing bits
351 if (last) {
352 if (m <= left) {
353 // Single source word
Pavel Pisa15afdd42007-10-16 01:29:55 -0700354 d0 >>= right;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 } else {
356 // 2 source words
357 d1 = FB_READL(src);
Pavel Pisa15afdd42007-10-16 01:29:55 -0700358 d1 = fb_rev_pixels_in_long(d1,
359 bswapmask);
360 d0 = d0>>right | d1<<left;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 }
Pavel Pisa15afdd42007-10-16 01:29:55 -0700362 d0 = fb_rev_pixels_in_long(d0, bswapmask);
363 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 }
365 }
366 }
367}
368
369void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
370{
371 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
372 u32 height = area->height, width = area->width;
373 unsigned long const bits_per_line = p->fix.line_length*8u;
374 unsigned long __iomem *dst = NULL, *src = NULL;
375 int bits = BITS_PER_LONG, bytes = bits >> 3;
376 int dst_idx = 0, src_idx = 0, rev_copy = 0;
Pavel Pisa779121e2007-10-16 01:29:21 -0700377 u32 bswapmask = fb_compute_bswapmask(p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
379 if (p->state != FBINFO_STATE_RUNNING)
380 return;
381
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 /* if the beginning of the target area might overlap with the end of
383 the source area, be have to copy the area reverse. */
384 if ((dy == sy && dx > sx) || (dy > sy)) {
385 dy += height;
386 sy += height;
387 rev_copy = 1;
388 }
389
390 // split the base of the framebuffer into a long-aligned address and the
391 // index of the first bit
392 dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
393 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
394 // add offset of source and target area
395 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
396 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
397
398 if (p->fbops->fb_sync)
399 p->fbops->fb_sync(p);
400
401 if (rev_copy) {
402 while (height--) {
403 dst_idx -= bits_per_line;
404 src_idx -= bits_per_line;
405 dst += dst_idx >> (ffs(bits) - 1);
406 dst_idx &= (bytes - 1);
407 src += src_idx >> (ffs(bits) - 1);
408 src_idx &= (bytes - 1);
Anton Vorontsove4c690e2008-04-28 02:14:49 -0700409 bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
Pavel Pisa779121e2007-10-16 01:29:21 -0700410 width*p->var.bits_per_pixel, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 }
412 } else {
413 while (height--) {
414 dst += dst_idx >> (ffs(bits) - 1);
415 dst_idx &= (bytes - 1);
416 src += src_idx >> (ffs(bits) - 1);
417 src_idx &= (bytes - 1);
Anton Vorontsove4c690e2008-04-28 02:14:49 -0700418 bitcpy(p, dst, dst_idx, src, src_idx, bits,
Pavel Pisa779121e2007-10-16 01:29:21 -0700419 width*p->var.bits_per_pixel, bswapmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 dst_idx += bits_per_line;
421 src_idx += bits_per_line;
422 }
423 }
424}
425
426EXPORT_SYMBOL(cfb_copyarea);
427
428MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
429MODULE_DESCRIPTION("Generic software accelerated copyarea");
430MODULE_LICENSE("GPL");
431