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