Merge tag 'vfs-6.7.misc' of gitolite.kernel.org:pub/scm/linux/kernel/git/vfs/vfs
[linux-block.git] / drivers / video / fbdev / core / 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>
1da177e4
LT
29#include <asm/types.h>
30#include <asm/io.h>
dc0e6e05 31#include "fb_draw.h"
1da177e4
LT
32
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
1da177e4
LT
41 /*
42 * Generic bitwise copy algorithm
43 */
44
45static void
00a9d699
MP
46bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
47 const unsigned long __iomem *src, unsigned src_idx, int bits,
e4c690e0 48 unsigned n, u32 bswapmask)
1da177e4
LT
49{
50 unsigned long first, last;
51 int const shift = dst_idx-src_idx;
00a9d699
MP
52
53#if 0
54 /*
55 * If you suspect bug in this function, compare it with this simple
56 * memmove implementation.
57 */
5b789da8
MP
58 memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
59 (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
00a9d699
MP
60 return;
61#endif
1da177e4 62
e4c690e0
AV
63 first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
64 last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
1da177e4
LT
65
66 if (!shift) {
67 // Same alignment for source and dest
68
69 if (dst_idx+n <= bits) {
70 // Single word
71 if (last)
72 first &= last;
73 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
74 } else {
75 // Multiple destination words
76
77 // Leading bits
78 if (first != ~0UL) {
79 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
80 dst++;
81 src++;
82 n -= bits - dst_idx;
83 }
84
85 // Main chunk
86 n /= bits;
87 while (n >= 8) {
88 FB_WRITEL(FB_READL(src++), dst++);
89 FB_WRITEL(FB_READL(src++), dst++);
90 FB_WRITEL(FB_READL(src++), dst++);
91 FB_WRITEL(FB_READL(src++), dst++);
92 FB_WRITEL(FB_READL(src++), dst++);
93 FB_WRITEL(FB_READL(src++), dst++);
94 FB_WRITEL(FB_READL(src++), dst++);
95 FB_WRITEL(FB_READL(src++), dst++);
96 n -= 8;
97 }
98 while (n--)
99 FB_WRITEL(FB_READL(src++), dst++);
100
101 // Trailing bits
102 if (last)
103 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
104 }
105 } else {
15afdd43 106 /* Different alignment for source and dest */
1da177e4
LT
107 unsigned long d0, d1;
108 int m;
1da177e4 109
00a9d699
MP
110 int const left = shift & (bits - 1);
111 int const right = -shift & (bits - 1);
1da177e4
LT
112
113 if (dst_idx+n <= bits) {
114 // Single destination word
115 if (last)
116 first &= last;
15afdd43
PP
117 d0 = FB_READL(src);
118 d0 = fb_rev_pixels_in_long(d0, bswapmask);
1da177e4
LT
119 if (shift > 0) {
120 // Single source word
00a9d699 121 d0 <<= left;
1da177e4
LT
122 } else if (src_idx+n <= bits) {
123 // Single source word
00a9d699 124 d0 >>= right;
1da177e4
LT
125 } else {
126 // 2 source words
15afdd43
PP
127 d1 = FB_READL(src + 1);
128 d1 = fb_rev_pixels_in_long(d1, bswapmask);
00a9d699 129 d0 = d0 >> right | d1 << left;
1da177e4 130 }
15afdd43
PP
131 d0 = fb_rev_pixels_in_long(d0, bswapmask);
132 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
1da177e4
LT
133 } else {
134 // Multiple destination words
135 /** We must always remember the last value read, because in case
136 SRC and DST overlap bitwise (e.g. when moving just one pixel in
137 1bpp), we always collect one full long for DST and that might
138 overlap with the current long from SRC. We store this value in
139 'd0'. */
140 d0 = FB_READL(src++);
15afdd43 141 d0 = fb_rev_pixels_in_long(d0, bswapmask);
1da177e4
LT
142 // Leading bits
143 if (shift > 0) {
144 // Single source word
15afdd43 145 d1 = d0;
00a9d699 146 d0 <<= left;
1da177e4
LT
147 n -= bits - dst_idx;
148 } else {
149 // 2 source words
150 d1 = FB_READL(src++);
15afdd43
PP
151 d1 = fb_rev_pixels_in_long(d1, bswapmask);
152
00a9d699 153 d0 = d0 >> right | d1 << left;
1da177e4
LT
154 n -= bits - dst_idx;
155 }
15afdd43
PP
156 d0 = fb_rev_pixels_in_long(d0, bswapmask);
157 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
158 d0 = d1;
00a9d699 159 dst++;
1da177e4
LT
160
161 // Main chunk
162 m = n % bits;
163 n /= bits;
15afdd43 164 while ((n >= 4) && !bswapmask) {
1da177e4 165 d1 = FB_READL(src++);
00a9d699 166 FB_WRITEL(d0 >> right | d1 << left, dst++);
1da177e4
LT
167 d0 = d1;
168 d1 = FB_READL(src++);
00a9d699 169 FB_WRITEL(d0 >> right | d1 << left, dst++);
1da177e4
LT
170 d0 = d1;
171 d1 = FB_READL(src++);
00a9d699 172 FB_WRITEL(d0 >> right | d1 << left, dst++);
1da177e4
LT
173 d0 = d1;
174 d1 = FB_READL(src++);
00a9d699 175 FB_WRITEL(d0 >> right | d1 << left, dst++);
1da177e4
LT
176 d0 = d1;
177 n -= 4;
178 }
179 while (n--) {
180 d1 = FB_READL(src++);
15afdd43 181 d1 = fb_rev_pixels_in_long(d1, bswapmask);
00a9d699 182 d0 = d0 >> right | d1 << left;
15afdd43
PP
183 d0 = fb_rev_pixels_in_long(d0, bswapmask);
184 FB_WRITEL(d0, dst++);
1da177e4
LT
185 d0 = d1;
186 }
187
188 // Trailing bits
00a9d699
MP
189 if (m) {
190 if (m <= bits - right) {
1da177e4 191 // Single source word
00a9d699 192 d0 >>= right;
1da177e4
LT
193 } else {
194 // 2 source words
195 d1 = FB_READL(src);
15afdd43
PP
196 d1 = fb_rev_pixels_in_long(d1,
197 bswapmask);
00a9d699 198 d0 = d0 >> right | d1 << left;
1da177e4 199 }
15afdd43
PP
200 d0 = fb_rev_pixels_in_long(d0, bswapmask);
201 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
1da177e4
LT
202 }
203 }
204 }
205}
206
207 /*
208 * Generic bitwise copy algorithm, operating backward
209 */
210
211static void
00a9d699
MP
212bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
213 const unsigned long __iomem *src, unsigned src_idx, int bits,
e4c690e0 214 unsigned n, u32 bswapmask)
1da177e4
LT
215{
216 unsigned long first, last;
217 int shift;
218
00a9d699
MP
219#if 0
220 /*
221 * If you suspect bug in this function, compare it with this simple
222 * memmove implementation.
223 */
5b789da8
MP
224 memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
225 (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
00a9d699
MP
226 return;
227#endif
228
229 dst += (dst_idx + n - 1) / bits;
230 src += (src_idx + n - 1) / bits;
231 dst_idx = (dst_idx + n - 1) % bits;
232 src_idx = (src_idx + n - 1) % bits;
1da177e4
LT
233
234 shift = dst_idx-src_idx;
235
00a9d699
MP
236 first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask);
237 last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask);
1da177e4
LT
238
239 if (!shift) {
240 // Same alignment for source and dest
241
242 if ((unsigned long)dst_idx+1 >= n) {
243 // Single word
00a9d699
MP
244 if (first)
245 last &= first;
246 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
1da177e4
LT
247 } else {
248 // Multiple destination words
249
250 // Leading bits
00a9d699 251 if (first) {
1da177e4
LT
252 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
253 dst--;
254 src--;
255 n -= dst_idx+1;
256 }
257
258 // Main chunk
259 n /= bits;
260 while (n >= 8) {
261 FB_WRITEL(FB_READL(src--), dst--);
262 FB_WRITEL(FB_READL(src--), dst--);
263 FB_WRITEL(FB_READL(src--), dst--);
264 FB_WRITEL(FB_READL(src--), dst--);
265 FB_WRITEL(FB_READL(src--), dst--);
266 FB_WRITEL(FB_READL(src--), dst--);
267 FB_WRITEL(FB_READL(src--), dst--);
268 FB_WRITEL(FB_READL(src--), dst--);
269 n -= 8;
270 }
271 while (n--)
272 FB_WRITEL(FB_READL(src--), dst--);
273
274 // Trailing bits
00a9d699 275 if (last != -1UL)
1da177e4
LT
276 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
277 }
278 } else {
279 // Different alignment for source and dest
15afdd43
PP
280 unsigned long d0, d1;
281 int m;
1da177e4 282
00a9d699
MP
283 int const left = shift & (bits-1);
284 int const right = -shift & (bits-1);
1da177e4
LT
285
286 if ((unsigned long)dst_idx+1 >= n) {
287 // Single destination word
00a9d699
MP
288 if (first)
289 last &= first;
15afdd43 290 d0 = FB_READL(src);
1da177e4
LT
291 if (shift < 0) {
292 // Single source word
00a9d699 293 d0 >>= right;
1da177e4
LT
294 } else if (1+(unsigned long)src_idx >= n) {
295 // Single source word
00a9d699 296 d0 <<= left;
1da177e4
LT
297 } else {
298 // 2 source words
15afdd43
PP
299 d1 = FB_READL(src - 1);
300 d1 = fb_rev_pixels_in_long(d1, bswapmask);
00a9d699 301 d0 = d0 << left | d1 >> right;
1da177e4 302 }
15afdd43 303 d0 = fb_rev_pixels_in_long(d0, bswapmask);
00a9d699 304 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
1da177e4
LT
305 } else {
306 // Multiple destination words
307 /** We must always remember the last value read, because in case
308 SRC and DST overlap bitwise (e.g. when moving just one pixel in
309 1bpp), we always collect one full long for DST and that might
310 overlap with the current long from SRC. We store this value in
311 'd0'. */
1da177e4
LT
312
313 d0 = FB_READL(src--);
15afdd43 314 d0 = fb_rev_pixels_in_long(d0, bswapmask);
1da177e4
LT
315 // Leading bits
316 if (shift < 0) {
317 // Single source word
15afdd43 318 d1 = d0;
00a9d699 319 d0 >>= right;
1da177e4
LT
320 } else {
321 // 2 source words
322 d1 = FB_READL(src--);
15afdd43 323 d1 = fb_rev_pixels_in_long(d1, bswapmask);
00a9d699 324 d0 = d0 << left | d1 >> right;
1da177e4 325 }
15afdd43 326 d0 = fb_rev_pixels_in_long(d0, bswapmask);
5b789da8
MP
327 if (!first)
328 FB_WRITEL(d0, dst);
329 else
330 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
15afdd43 331 d0 = d1;
1da177e4
LT
332 dst--;
333 n -= dst_idx+1;
334
335 // Main chunk
336 m = n % bits;
337 n /= bits;
15afdd43 338 while ((n >= 4) && !bswapmask) {
1da177e4 339 d1 = FB_READL(src--);
00a9d699 340 FB_WRITEL(d0 << left | d1 >> right, dst--);
1da177e4
LT
341 d0 = d1;
342 d1 = FB_READL(src--);
00a9d699 343 FB_WRITEL(d0 << left | d1 >> right, dst--);
1da177e4
LT
344 d0 = d1;
345 d1 = FB_READL(src--);
00a9d699 346 FB_WRITEL(d0 << left | d1 >> right, dst--);
1da177e4
LT
347 d0 = d1;
348 d1 = FB_READL(src--);
00a9d699 349 FB_WRITEL(d0 << left | d1 >> right, dst--);
1da177e4
LT
350 d0 = d1;
351 n -= 4;
352 }
353 while (n--) {
354 d1 = FB_READL(src--);
15afdd43 355 d1 = fb_rev_pixels_in_long(d1, bswapmask);
00a9d699 356 d0 = d0 << left | d1 >> right;
15afdd43
PP
357 d0 = fb_rev_pixels_in_long(d0, bswapmask);
358 FB_WRITEL(d0, dst--);
1da177e4
LT
359 d0 = d1;
360 }
361
362 // Trailing bits
00a9d699
MP
363 if (m) {
364 if (m <= bits - left) {
1da177e4 365 // Single source word
00a9d699 366 d0 <<= left;
1da177e4
LT
367 } else {
368 // 2 source words
369 d1 = FB_READL(src);
15afdd43
PP
370 d1 = fb_rev_pixels_in_long(d1,
371 bswapmask);
00a9d699 372 d0 = d0 << left | d1 >> right;
1da177e4 373 }
15afdd43
PP
374 d0 = fb_rev_pixels_in_long(d0, bswapmask);
375 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
1da177e4
LT
376 }
377 }
378 }
379}
380
381void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
382{
383 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
384 u32 height = area->height, width = area->width;
7f33df94 385 unsigned int const bits_per_line = p->fix.line_length * 8u;
00a9d699 386 unsigned long __iomem *base = NULL;
1da177e4 387 int bits = BITS_PER_LONG, bytes = bits >> 3;
00a9d699 388 unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
779121e9 389 u32 bswapmask = fb_compute_bswapmask(p);
1da177e4
LT
390
391 if (p->state != FBINFO_STATE_RUNNING)
392 return;
393
1da177e4
LT
394 /* if the beginning of the target area might overlap with the end of
395 the source area, be have to copy the area reverse. */
396 if ((dy == sy && dx > sx) || (dy > sy)) {
397 dy += height;
398 sy += height;
399 rev_copy = 1;
400 }
401
402 // split the base of the framebuffer into a long-aligned address and the
403 // index of the first bit
00a9d699 404 base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
1da177e4
LT
405 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
406 // add offset of source and target area
407 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
408 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
409
410 if (p->fbops->fb_sync)
411 p->fbops->fb_sync(p);
412
413 if (rev_copy) {
414 while (height--) {
415 dst_idx -= bits_per_line;
416 src_idx -= bits_per_line;
00a9d699
MP
417 bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
418 base + (src_idx / bits), src_idx % bits, bits,
779121e9 419 width*p->var.bits_per_pixel, bswapmask);
1da177e4
LT
420 }
421 } else {
422 while (height--) {
00a9d699
MP
423 bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
424 base + (src_idx / bits), src_idx % bits, bits,
779121e9 425 width*p->var.bits_per_pixel, bswapmask);
1da177e4
LT
426 dst_idx += bits_per_line;
427 src_idx += bits_per_line;
428 }
429 }
430}
431
432EXPORT_SYMBOL(cfb_copyarea);
433
434MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
435MODULE_DESCRIPTION("Generic software accelerated copyarea");
436MODULE_LICENSE("GPL");
437