Automatic merge of rsync://rsync.kernel.org/pub/scm/linux/kernel/git/gregkh/driver...
[linux-2.6/verdex.git] / drivers / video / cfbcopyarea.c
blob67711f7b11b14229f6cd8604c73c05b48c966b3c
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/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>
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
45 * Compose two values, using a bitmask as decision value
46 * This is equivalent to (a & mask) | (b & ~mask)
49 static inline unsigned long
50 comp(unsigned long a, unsigned long b, unsigned long mask)
52 return ((a ^ b) & mask) ^ b;
56 * Generic bitwise copy algorithm
59 static void
60 bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
61 int src_idx, int bits, unsigned n)
63 unsigned long first, last;
64 int const shift = dst_idx-src_idx;
65 int left, right;
67 first = ~0UL >> dst_idx;
68 last = ~(~0UL >> ((dst_idx+n) % bits));
70 if (!shift) {
71 // Same alignment for source and dest
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
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;
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;
102 while (n--)
103 FB_WRITEL(FB_READL(src++), dst++);
105 // Trailing bits
106 if (last)
107 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
109 } else {
110 unsigned long d0, d1;
111 int m;
112 // Different alignment for source and dest
114 right = shift & (bits - 1);
115 left = -shift & (bits - 1);
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);
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;
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;
174 while (n--) {
175 d1 = FB_READL(src++);
176 FB_WRITEL(d0 << left | d1 >> right, dst++);
177 d0 = d1;
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);
196 * Generic bitwise copy algorithm, operating backward
199 static void
200 bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
201 int src_idx, int bits, unsigned n)
203 unsigned long first, last;
204 int shift;
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;
217 shift = dst_idx-src_idx;
219 first = ~0UL << (bits - 1 - dst_idx);
220 last = ~(~0UL << (bits - 1 - ((dst_idx-n) % bits)));
222 if (!shift) {
223 // Same alignment for source and dest
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
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;
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;
254 while (n--)
255 FB_WRITEL(FB_READL(src--), dst--);
257 // Trailing bits
258 if (last)
259 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
261 } else {
262 // Different alignment for source and dest
264 int const left = -shift & (bits-1);
265 int const right = shift & (bits-1);
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);
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;
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;
302 dst--;
303 n -= dst_idx+1;
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;
323 while (n--) {
324 d1 = FB_READL(src--);
325 FB_WRITEL(d0 >> right | d1 << left, dst--);
326 d0 = d1;
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);
344 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
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;
352 int x2, y2, vxres, vyres;
354 if (p->state != FBINFO_STATE_RUNNING)
355 return;
357 /* We want rotation but lack hardware to do it for us. */
358 if (!p->fbops->fb_rotate && p->var.rotate) {
361 vxres = p->var.xres_virtual;
362 vyres = p->var.yres_virtual;
364 if (area->dx > vxres || area->sx > vxres ||
365 area->dy > vyres || area->sy > vyres)
366 return;
368 /* clip the destination
369 * We could use hardware clipping but on many cards you get around
370 * hardware clipping by writing to framebuffer directly.
372 x2 = area->dx + area->width;
373 y2 = area->dy + area->height;
374 dx = area->dx > 0 ? area->dx : 0;
375 dy = area->dy > 0 ? area->dy : 0;
376 x2 = x2 < vxres ? x2 : vxres;
377 y2 = y2 < vyres ? y2 : vyres;
378 width = x2 - dx;
379 height = y2 - dy;
381 if ((width==0) ||(height==0))
382 return;
384 /* update sx1,sy1 */
385 sx += (dx - area->dx);
386 sy += (dy - area->dy);
388 /* the source must be completely inside the virtual screen */
389 if (sx < 0 || sy < 0 || (sx + width) > vxres || (sy + height) > vyres)
390 return;
392 /* if the beginning of the target area might overlap with the end of
393 the source area, be have to copy the area reverse. */
394 if ((dy == sy && dx > sx) || (dy > sy)) {
395 dy += height;
396 sy += height;
397 rev_copy = 1;
400 // split the base of the framebuffer into a long-aligned address and the
401 // index of the first bit
402 dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
403 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
404 // add offset of source and target area
405 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
406 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
408 if (p->fbops->fb_sync)
409 p->fbops->fb_sync(p);
411 if (rev_copy) {
412 while (height--) {
413 dst_idx -= bits_per_line;
414 src_idx -= bits_per_line;
415 dst += dst_idx >> (ffs(bits) - 1);
416 dst_idx &= (bytes - 1);
417 src += src_idx >> (ffs(bits) - 1);
418 src_idx &= (bytes - 1);
419 bitcpy_rev(dst, dst_idx, src, src_idx, bits,
420 width*p->var.bits_per_pixel);
422 } else {
423 while (height--) {
424 dst += dst_idx >> (ffs(bits) - 1);
425 dst_idx &= (bytes - 1);
426 src += src_idx >> (ffs(bits) - 1);
427 src_idx &= (bytes - 1);
428 bitcpy(dst, dst_idx, src, src_idx, bits,
429 width*p->var.bits_per_pixel);
430 dst_idx += bits_per_line;
431 src_idx += bits_per_line;
436 EXPORT_SYMBOL(cfb_copyarea);
438 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
439 MODULE_DESCRIPTION("Generic software accelerated copyarea");
440 MODULE_LICENSE("GPL");