aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/cfbcopyarea.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/video/cfbcopyarea.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/video/cfbcopyarea.c')
-rw-r--r--drivers/video/cfbcopyarea.c441
1 files changed, 441 insertions, 0 deletions
diff --git a/drivers/video/cfbcopyarea.c b/drivers/video/cfbcopyarea.c
new file mode 100644
index 000000000000..67711f7b11b1
--- /dev/null
+++ b/drivers/video/cfbcopyarea.c
@@ -0,0 +1,441 @@
1/*
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;
352 int x2, y2, vxres, vyres;
353
354 if (p->state != FBINFO_STATE_RUNNING)
355 return;
356
357 /* We want rotation but lack hardware to do it for us. */
358 if (!p->fbops->fb_rotate && p->var.rotate) {
359 }
360
361 vxres = p->var.xres_virtual;
362 vyres = p->var.yres_virtual;
363
364 if (area->dx > vxres || area->sx > vxres ||
365 area->dy > vyres || area->sy > vyres)
366 return;
367
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.
371 */
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;
380
381 if ((width==0) ||(height==0))
382 return;
383
384 /* update sx1,sy1 */
385 sx += (dx - area->dx);
386 sy += (dy - area->dy);
387
388 /* the source must be completely inside the virtual screen */
389 if (sx < 0 || sy < 0 || (sx + width) > vxres || (sy + height) > vyres)
390 return;
391
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;
398 }
399
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;
407
408 if (p->fbops->fb_sync)
409 p->fbops->fb_sync(p);
410
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);
421 }
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;
432 }
433 }
434}
435
436EXPORT_SYMBOL(cfb_copyarea);
437
438MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
439MODULE_DESCRIPTION("Generic software accelerated copyarea");
440MODULE_LICENSE("GPL");
441