[PATCH] ALSA: Fix re-use of va_list
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / video / cfbcopyarea.c
blob6faea4034e3db7dccd666bb032c73a1fece237a3
1 /*
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
8 * more details.
10 * NOTES:
12 * This is for cfb packed pixels. Iplan and such are incorporated in the
13 * drivers that need them.
15 * FIXME
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.
27 #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>
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
44 * Compose two values, using a bitmask as decision value
45 * This is equivalent to (a & mask) | (b & ~mask)
48 static inline unsigned long
49 comp(unsigned long a, unsigned long b, unsigned long mask)
51 return ((a ^ b) & mask) ^ b;
55 * Generic bitwise copy algorithm
58 static void
59 bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
60 int src_idx, int bits, unsigned n)
62 unsigned long first, last;
63 int const shift = dst_idx-src_idx;
64 int left, right;
66 first = FB_SHIFT_HIGH(~0UL, dst_idx);
67 last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
69 if (!shift) {
70 // Same alignment for source and dest
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
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;
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;
101 while (n--)
102 FB_WRITEL(FB_READL(src++), dst++);
104 // Trailing bits
105 if (last)
106 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
108 } else {
109 unsigned long d0, d1;
110 int m;
111 // Different alignment for source and dest
113 right = shift & (bits - 1);
114 left = -shift & (bits - 1);
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);
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;
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;
173 while (n--) {
174 d1 = FB_READL(src++);
175 FB_WRITEL(d0 << left | d1 >> right, dst++);
176 d0 = d1;
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);
195 * Generic bitwise copy algorithm, operating backward
198 static void
199 bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
200 int src_idx, int bits, unsigned n)
202 unsigned long first, last;
203 int shift;
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;
216 shift = dst_idx-src_idx;
218 first = FB_SHIFT_LOW(~0UL, bits - 1 - dst_idx);
219 last = ~(FB_SHIFT_LOW(~0UL, bits - 1 - ((dst_idx-n) % bits)));
221 if (!shift) {
222 // Same alignment for source and dest
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
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;
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;
253 while (n--)
254 FB_WRITEL(FB_READL(src--), dst--);
256 // Trailing bits
257 if (last)
258 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
260 } else {
261 // Different alignment for source and dest
263 int const left = -shift & (bits-1);
264 int const right = shift & (bits-1);
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);
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;
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;
301 dst--;
302 n -= dst_idx+1;
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;
322 while (n--) {
323 d1 = FB_READL(src--);
324 FB_WRITEL(d0 >> right | d1 << left, dst--);
325 d0 = d1;
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);
343 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
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;
352 if (p->state != FBINFO_STATE_RUNNING)
353 return;
355 /* 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;
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;
371 if (p->fbops->fb_sync)
372 p->fbops->fb_sync(p);
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);
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;
399 EXPORT_SYMBOL(cfb_copyarea);
401 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
402 MODULE_DESCRIPTION("Generic software accelerated copyarea");
403 MODULE_LICENSE("GPL");