blob: 6faea4034e3db7dccd666bb032c73a1fece237a3 [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
25
26
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <linux/module.h>
28#include <linux/kernel.h>
29#include <linux/string.h>
30#include <linux/fb.h>
31#include <linux/slab.h>
32#include <asm/types.h>
33#include <asm/io.h>
34
35#if BITS_PER_LONG == 32
36# define FB_WRITEL fb_writel
37# define FB_READL fb_readl
38#else
39# define FB_WRITEL fb_writeq
40# define FB_READL fb_readq
41#endif
42
43 /*
44 * Compose two values, using a bitmask as decision value
45 * This is equivalent to (a & mask) | (b & ~mask)
46 */
47
48static inline unsigned long
49comp(unsigned long a, unsigned long b, unsigned long mask)
50{
51 return ((a ^ b) & mask) ^ b;
52}
53
54 /*
55 * Generic bitwise copy algorithm
56 */
57
58static void
59bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
60 int src_idx, int bits, unsigned n)
61{
62 unsigned long first, last;
63 int const shift = dst_idx-src_idx;
64 int left, right;
65
Antonino A. Daplasbe0d9b62005-12-12 22:17:21 -080066 first = FB_SHIFT_HIGH(~0UL, dst_idx);
67 last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
69 if (!shift) {
70 // Same alignment for source and dest
71
72 if (dst_idx+n <= bits) {
73 // Single word
74 if (last)
75 first &= last;
76 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
77 } else {
78 // Multiple destination words
79
80 // Leading bits
81 if (first != ~0UL) {
82 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
83 dst++;
84 src++;
85 n -= bits - dst_idx;
86 }
87
88 // Main chunk
89 n /= bits;
90 while (n >= 8) {
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 FB_WRITEL(FB_READL(src++), dst++);
97 FB_WRITEL(FB_READL(src++), dst++);
98 FB_WRITEL(FB_READL(src++), dst++);
99 n -= 8;
100 }
101 while (n--)
102 FB_WRITEL(FB_READL(src++), dst++);
103
104 // Trailing bits
105 if (last)
106 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
107 }
108 } else {
109 unsigned long d0, d1;
110 int m;
111 // Different alignment for source and dest
112
113 right = shift & (bits - 1);
114 left = -shift & (bits - 1);
115
116 if (dst_idx+n <= bits) {
117 // Single destination word
118 if (last)
119 first &= last;
120 if (shift > 0) {
121 // Single source word
122 FB_WRITEL( comp( FB_READL(src) >> right, FB_READL(dst), first), dst);
123 } else if (src_idx+n <= bits) {
124 // Single source word
125 FB_WRITEL( comp(FB_READL(src) << left, FB_READL(dst), first), dst);
126 } else {
127 // 2 source words
128 d0 = FB_READL(src++);
129 d1 = FB_READL(src);
130 FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
131 }
132 } else {
133 // Multiple destination words
134 /** We must always remember the last value read, because in case
135 SRC and DST overlap bitwise (e.g. when moving just one pixel in
136 1bpp), we always collect one full long for DST and that might
137 overlap with the current long from SRC. We store this value in
138 'd0'. */
139 d0 = FB_READL(src++);
140 // Leading bits
141 if (shift > 0) {
142 // Single source word
143 FB_WRITEL( comp(d0 >> right, FB_READL(dst), first), dst);
144 dst++;
145 n -= bits - dst_idx;
146 } else {
147 // 2 source words
148 d1 = FB_READL(src++);
149 FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
150 d0 = d1;
151 dst++;
152 n -= bits - dst_idx;
153 }
154
155 // Main chunk
156 m = n % bits;
157 n /= bits;
158 while (n >= 4) {
159 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++);
175 FB_WRITEL(d0 << left | d1 >> right, dst++);
176 d0 = d1;
177 }
178
179 // Trailing bits
180 if (last) {
181 if (m <= right) {
182 // Single source word
183 FB_WRITEL( comp(d0 << left, FB_READL(dst), last), dst);
184 } else {
185 // 2 source words
186 d1 = FB_READL(src);
187 FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), last), dst);
188 }
189 }
190 }
191 }
192}
193
194 /*
195 * Generic bitwise copy algorithm, operating backward
196 */
197
198static void
199bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
200 int src_idx, int bits, unsigned n)
201{
202 unsigned long first, last;
203 int shift;
204
205 dst += (n-1)/bits;
206 src += (n-1)/bits;
207 if ((n-1) % bits) {
208 dst_idx += (n-1) % bits;
209 dst += dst_idx >> (ffs(bits) - 1);
210 dst_idx &= bits - 1;
211 src_idx += (n-1) % bits;
212 src += src_idx >> (ffs(bits) - 1);
213 src_idx &= bits - 1;
214 }
215
216 shift = dst_idx-src_idx;
217
Antonino A. Daplasbe0d9b62005-12-12 22:17:21 -0800218 first = FB_SHIFT_LOW(~0UL, bits - 1 - dst_idx);
219 last = ~(FB_SHIFT_LOW(~0UL, bits - 1 - ((dst_idx-n) % bits)));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
221 if (!shift) {
222 // Same alignment for source and dest
223
224 if ((unsigned long)dst_idx+1 >= n) {
225 // Single word
226 if (last)
227 first &= last;
228 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
229 } else {
230 // Multiple destination words
231
232 // Leading bits
233 if (first != ~0UL) {
234 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
235 dst--;
236 src--;
237 n -= dst_idx+1;
238 }
239
240 // Main chunk
241 n /= bits;
242 while (n >= 8) {
243 FB_WRITEL(FB_READL(src--), dst--);
244 FB_WRITEL(FB_READL(src--), dst--);
245 FB_WRITEL(FB_READL(src--), dst--);
246 FB_WRITEL(FB_READL(src--), dst--);
247 FB_WRITEL(FB_READL(src--), dst--);
248 FB_WRITEL(FB_READL(src--), dst--);
249 FB_WRITEL(FB_READL(src--), dst--);
250 FB_WRITEL(FB_READL(src--), dst--);
251 n -= 8;
252 }
253 while (n--)
254 FB_WRITEL(FB_READL(src--), dst--);
255
256 // Trailing bits
257 if (last)
258 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
259 }
260 } else {
261 // Different alignment for source and dest
262
263 int const left = -shift & (bits-1);
264 int const right = shift & (bits-1);
265
266 if ((unsigned long)dst_idx+1 >= n) {
267 // Single destination word
268 if (last)
269 first &= last;
270 if (shift < 0) {
271 // Single source word
272 FB_WRITEL( comp( FB_READL(src)<<left, FB_READL(dst), first), dst);
273 } else if (1+(unsigned long)src_idx >= n) {
274 // Single source word
275 FB_WRITEL( comp( FB_READL(src)>>right, FB_READL(dst), first), dst);
276 } else {
277 // 2 source words
278 FB_WRITEL( comp( (FB_READL(src)>>right | FB_READL(src-1)<<left), FB_READL(dst), first), dst);
279 }
280 } else {
281 // Multiple destination words
282 /** We must always remember the last value read, because in case
283 SRC and DST overlap bitwise (e.g. when moving just one pixel in
284 1bpp), we always collect one full long for DST and that might
285 overlap with the current long from SRC. We store this value in
286 'd0'. */
287 unsigned long d0, d1;
288 int m;
289
290 d0 = FB_READL(src--);
291 // Leading bits
292 if (shift < 0) {
293 // Single source word
294 FB_WRITEL( comp( (d0 << left), FB_READL(dst), first), dst);
295 } else {
296 // 2 source words
297 d1 = FB_READL(src--);
298 FB_WRITEL( comp( (d0>>right | d1<<left), FB_READL(dst), first), dst);
299 d0 = d1;
300 }
301 dst--;
302 n -= dst_idx+1;
303
304 // Main chunk
305 m = n % bits;
306 n /= bits;
307 while (n >= 4) {
308 d1 = FB_READL(src--);
309 FB_WRITEL(d0 >> right | d1 << left, dst--);
310 d0 = d1;
311 d1 = FB_READL(src--);
312 FB_WRITEL(d0 >> right | d1 << left, dst--);
313 d0 = d1;
314 d1 = FB_READL(src--);
315 FB_WRITEL(d0 >> right | d1 << left, dst--);
316 d0 = d1;
317 d1 = FB_READL(src--);
318 FB_WRITEL(d0 >> right | d1 << left, dst--);
319 d0 = d1;
320 n -= 4;
321 }
322 while (n--) {
323 d1 = FB_READL(src--);
324 FB_WRITEL(d0 >> right | d1 << left, dst--);
325 d0 = d1;
326 }
327
328 // Trailing bits
329 if (last) {
330 if (m <= left) {
331 // Single source word
332 FB_WRITEL( comp(d0 >> right, FB_READL(dst), last), dst);
333 } else {
334 // 2 source words
335 d1 = FB_READL(src);
336 FB_WRITEL( comp(d0>>right | d1<<left, FB_READL(dst), last), dst);
337 }
338 }
339 }
340 }
341}
342
343void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
344{
345 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
346 u32 height = area->height, width = area->width;
347 unsigned long const bits_per_line = p->fix.line_length*8u;
348 unsigned long __iomem *dst = NULL, *src = NULL;
349 int bits = BITS_PER_LONG, bytes = bits >> 3;
350 int dst_idx = 0, src_idx = 0, rev_copy = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351
352 if (p->state != FBINFO_STATE_RUNNING)
353 return;
354
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 /* if the beginning of the target area might overlap with the end of
356 the source area, be have to copy the area reverse. */
357 if ((dy == sy && dx > sx) || (dy > sy)) {
358 dy += height;
359 sy += height;
360 rev_copy = 1;
361 }
362
363 // split the base of the framebuffer into a long-aligned address and the
364 // index of the first bit
365 dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
366 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
367 // add offset of source and target area
368 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
369 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
370
371 if (p->fbops->fb_sync)
372 p->fbops->fb_sync(p);
373
374 if (rev_copy) {
375 while (height--) {
376 dst_idx -= bits_per_line;
377 src_idx -= bits_per_line;
378 dst += dst_idx >> (ffs(bits) - 1);
379 dst_idx &= (bytes - 1);
380 src += src_idx >> (ffs(bits) - 1);
381 src_idx &= (bytes - 1);
382 bitcpy_rev(dst, dst_idx, src, src_idx, bits,
383 width*p->var.bits_per_pixel);
384 }
385 } else {
386 while (height--) {
387 dst += dst_idx >> (ffs(bits) - 1);
388 dst_idx &= (bytes - 1);
389 src += src_idx >> (ffs(bits) - 1);
390 src_idx &= (bytes - 1);
391 bitcpy(dst, dst_idx, src, src_idx, bits,
392 width*p->var.bits_per_pixel);
393 dst_idx += bits_per_line;
394 src_idx += bits_per_line;
395 }
396 }
397}
398
399EXPORT_SYMBOL(cfb_copyarea);
400
401MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
402MODULE_DESCRIPTION("Generic software accelerated copyarea");
403MODULE_LICENSE("GPL");
404