2 * Generic function for frame buffer with packed pixels of any depth.
4 * Copyright (C) 1999-2005 James Simmons <jsimmons@www.infradead.org>
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
12 * This is for cfb packed pixels. Iplan and such are incorporated in the
13 * drivers that need them.
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.
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.
25 #include <linux/module.h>
26 #include <linux/kernel.h>
27 #include <linux/string.h>
29 #include <linux/slab.h>
30 #include <asm/types.h>
34 #if BITS_PER_LONG == 32
35 # define FB_WRITEL fb_writel
36 # define FB_READL fb_readl
38 # define FB_WRITEL fb_writeq
39 # define FB_READL fb_readq
43 * Generic bitwise copy algorithm
47 bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
48 int src_idx, int bits, unsigned n)
50 unsigned long first, last;
51 int const shift = dst_idx-src_idx;
54 first = FB_SHIFT_HIGH(~0UL, dst_idx);
55 last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
58 // Same alignment for source and dest
60 if (dst_idx+n <= bits) {
64 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
66 // Multiple destination words
70 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
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++);
90 FB_WRITEL(FB_READL(src++), dst++);
94 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
99 // Different alignment for source and dest
101 right = shift & (bits - 1);
102 left = -shift & (bits - 1);
104 if (dst_idx+n <= bits) {
105 // Single destination word
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);
116 d0 = FB_READL(src++);
118 FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
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
127 d0 = FB_READL(src++);
130 // Single source word
131 FB_WRITEL( comp(d0 >> right, FB_READL(dst), first), dst);
136 d1 = FB_READL(src++);
137 FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), first), dst);
147 d1 = FB_READL(src++);
148 FB_WRITEL(d0 << left | d1 >> right, dst++);
150 d1 = FB_READL(src++);
151 FB_WRITEL(d0 << left | d1 >> right, dst++);
153 d1 = FB_READL(src++);
154 FB_WRITEL(d0 << left | d1 >> right, dst++);
156 d1 = FB_READL(src++);
157 FB_WRITEL(d0 << left | d1 >> right, dst++);
162 d1 = FB_READL(src++);
163 FB_WRITEL(d0 << left | d1 >> right, dst++);
170 // Single source word
171 FB_WRITEL( comp(d0 << left, FB_READL(dst), last), dst);
175 FB_WRITEL( comp(d0<<left | d1>>right, FB_READL(dst), last), dst);
183 * Generic bitwise copy algorithm, operating backward
187 bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
188 int src_idx, int bits, unsigned n)
190 unsigned long first, last;
196 dst_idx += (n-1) % bits;
197 dst += dst_idx >> (ffs(bits) - 1);
199 src_idx += (n-1) % bits;
200 src += src_idx >> (ffs(bits) - 1);
204 shift = dst_idx-src_idx;
206 first = FB_SHIFT_LOW(~0UL, bits - 1 - dst_idx);
207 last = ~(FB_SHIFT_LOW(~0UL, bits - 1 - ((dst_idx-n) % bits)));
210 // Same alignment for source and dest
212 if ((unsigned long)dst_idx+1 >= n) {
216 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
218 // Multiple destination words
222 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
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--);
242 FB_WRITEL(FB_READL(src--), dst--);
246 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
249 // Different alignment for source and dest
251 int const left = -shift & (bits-1);
252 int const right = shift & (bits-1);
254 if ((unsigned long)dst_idx+1 >= n) {
255 // Single destination word
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);
266 FB_WRITEL( comp( (FB_READL(src)>>right | FB_READL(src-1)<<left), FB_READL(dst), first), dst);
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
275 unsigned long d0, d1;
278 d0 = FB_READL(src--);
281 // Single source word
282 FB_WRITEL( comp( (d0 << left), FB_READL(dst), first), dst);
285 d1 = FB_READL(src--);
286 FB_WRITEL( comp( (d0>>right | d1<<left), FB_READL(dst), first), dst);
296 d1 = FB_READL(src--);
297 FB_WRITEL(d0 >> right | d1 << left, dst--);
299 d1 = FB_READL(src--);
300 FB_WRITEL(d0 >> right | d1 << left, dst--);
302 d1 = FB_READL(src--);
303 FB_WRITEL(d0 >> right | d1 << left, dst--);
305 d1 = FB_READL(src--);
306 FB_WRITEL(d0 >> right | d1 << left, dst--);
311 d1 = FB_READL(src--);
312 FB_WRITEL(d0 >> right | d1 << left, dst--);
319 // Single source word
320 FB_WRITEL( comp(d0 >> right, FB_READL(dst), last), dst);
324 FB_WRITEL( comp(d0>>right | d1<<left, FB_READL(dst), last), dst);
331 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
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;
340 if (p->state != FBINFO_STATE_RUNNING)
343 /* 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)) {
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;
359 if (p->fbops->fb_sync)
360 p->fbops->fb_sync(p);
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);
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;
387 EXPORT_SYMBOL(cfb_copyarea);
389 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
390 MODULE_DESCRIPTION("Generic software accelerated copyarea");
391 MODULE_LICENSE("GPL");