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