soc: Introduce drivers/soc place-holder for SOC specific drivers
[deliverable/linux.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>
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 */
58 fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
59 (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
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 */
224 fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
225 (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
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
PP
326 d0 = fb_rev_pixels_in_long(d0, bswapmask);
327 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
328 d0 = d1;
1da177e4
LT
329 dst--;
330 n -= dst_idx+1;
331
332 // Main chunk
333 m = n % bits;
334 n /= bits;
15afdd43 335 while ((n >= 4) && !bswapmask) {
1da177e4 336 d1 = FB_READL(src--);
00a9d699 337 FB_WRITEL(d0 << left | d1 >> right, dst--);
1da177e4
LT
338 d0 = d1;
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 n -= 4;
349 }
350 while (n--) {
351 d1 = FB_READL(src--);
15afdd43 352 d1 = fb_rev_pixels_in_long(d1, bswapmask);
00a9d699 353 d0 = d0 << left | d1 >> right;
15afdd43
PP
354 d0 = fb_rev_pixels_in_long(d0, bswapmask);
355 FB_WRITEL(d0, dst--);
1da177e4
LT
356 d0 = d1;
357 }
358
359 // Trailing bits
00a9d699
MP
360 if (m) {
361 if (m <= bits - left) {
1da177e4 362 // Single source word
00a9d699 363 d0 <<= left;
1da177e4
LT
364 } else {
365 // 2 source words
366 d1 = FB_READL(src);
15afdd43
PP
367 d1 = fb_rev_pixels_in_long(d1,
368 bswapmask);
00a9d699 369 d0 = d0 << left | d1 >> right;
1da177e4 370 }
15afdd43
PP
371 d0 = fb_rev_pixels_in_long(d0, bswapmask);
372 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
1da177e4
LT
373 }
374 }
375 }
376}
377
378void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
379{
380 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
381 u32 height = area->height, width = area->width;
382 unsigned long const bits_per_line = p->fix.line_length*8u;
00a9d699 383 unsigned long __iomem *base = NULL;
1da177e4 384 int bits = BITS_PER_LONG, bytes = bits >> 3;
00a9d699 385 unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
779121e9 386 u32 bswapmask = fb_compute_bswapmask(p);
1da177e4
LT
387
388 if (p->state != FBINFO_STATE_RUNNING)
389 return;
390
1da177e4
LT
391 /* if the beginning of the target area might overlap with the end of
392 the source area, be have to copy the area reverse. */
393 if ((dy == sy && dx > sx) || (dy > sy)) {
394 dy += height;
395 sy += height;
396 rev_copy = 1;
397 }
398
399 // split the base of the framebuffer into a long-aligned address and the
400 // index of the first bit
00a9d699 401 base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
1da177e4
LT
402 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
403 // add offset of source and target area
404 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
405 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
406
407 if (p->fbops->fb_sync)
408 p->fbops->fb_sync(p);
409
410 if (rev_copy) {
411 while (height--) {
412 dst_idx -= bits_per_line;
413 src_idx -= bits_per_line;
00a9d699
MP
414 bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
415 base + (src_idx / bits), src_idx % bits, bits,
779121e9 416 width*p->var.bits_per_pixel, bswapmask);
1da177e4
LT
417 }
418 } else {
419 while (height--) {
00a9d699
MP
420 bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
421 base + (src_idx / bits), src_idx % bits, bits,
779121e9 422 width*p->var.bits_per_pixel, bswapmask);
1da177e4
LT
423 dst_idx += bits_per_line;
424 src_idx += bits_per_line;
425 }
426 }
427}
428
429EXPORT_SYMBOL(cfb_copyarea);
430
431MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
432MODULE_DESCRIPTION("Generic software accelerated copyarea");
433MODULE_LICENSE("GPL");
434
This page took 1.126281 seconds and 5 git commands to generate.