Blackfin char driver for Blackfin on-chip OTP memory (v3)
[linux-2.6/zen-sources.git] / drivers / video / cfbcopyarea.c
blobb07e419b12d289f79dfeb2a34f1e87c66b12c971
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.
25 #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>
32 #include "fb_draw.h"
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
43 * Generic bitwise copy algorithm
46 static void
47 bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
48 int src_idx, int bits, unsigned n, u32 bswapmask)
50 unsigned long first, last;
51 int const shift = dst_idx-src_idx;
52 int left, right;
54 first = fb_shifted_pixels_mask_long(dst_idx, bswapmask);
55 last = ~fb_shifted_pixels_mask_long((dst_idx+n) % bits, bswapmask);
57 if (!shift) {
58 // Same alignment for source and dest
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
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;
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;
89 while (n--)
90 FB_WRITEL(FB_READL(src++), dst++);
92 // Trailing bits
93 if (last)
94 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
96 } else {
97 /* Different alignment for source and dest */
98 unsigned long d0, d1;
99 int m;
101 right = shift & (bits - 1);
102 left = -shift & (bits - 1);
103 bswapmask &= shift;
105 if (dst_idx+n <= bits) {
106 // Single destination word
107 if (last)
108 first &= last;
109 d0 = FB_READL(src);
110 d0 = fb_rev_pixels_in_long(d0, bswapmask);
111 if (shift > 0) {
112 // Single source word
113 d0 >>= right;
114 } else if (src_idx+n <= bits) {
115 // Single source word
116 d0 <<= left;;
117 } else {
118 // 2 source words
119 d1 = FB_READL(src + 1);
120 d1 = fb_rev_pixels_in_long(d1, bswapmask);
121 d0 = d0<<left | d1>>right;
123 d0 = fb_rev_pixels_in_long(d0, bswapmask);
124 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
125 } else {
126 // Multiple destination words
127 /** We must always remember the last value read, because in case
128 SRC and DST overlap bitwise (e.g. when moving just one pixel in
129 1bpp), we always collect one full long for DST and that might
130 overlap with the current long from SRC. We store this value in
131 'd0'. */
132 d0 = FB_READL(src++);
133 d0 = fb_rev_pixels_in_long(d0, bswapmask);
134 // Leading bits
135 if (shift > 0) {
136 // Single source word
137 d1 = d0;
138 d0 >>= right;
139 dst++;
140 n -= bits - dst_idx;
141 } else {
142 // 2 source words
143 d1 = FB_READL(src++);
144 d1 = fb_rev_pixels_in_long(d1, bswapmask);
146 d0 = d0<<left | d1>>right;
147 dst++;
148 n -= bits - dst_idx;
150 d0 = fb_rev_pixels_in_long(d0, bswapmask);
151 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
152 d0 = d1;
154 // Main chunk
155 m = n % bits;
156 n /= bits;
157 while ((n >= 4) && !bswapmask) {
158 d1 = FB_READL(src++);
159 FB_WRITEL(d0 << left | d1 >> right, dst++);
160 d0 = d1;
161 d1 = FB_READL(src++);
162 FB_WRITEL(d0 << left | d1 >> right, dst++);
163 d0 = d1;
164 d1 = FB_READL(src++);
165 FB_WRITEL(d0 << left | d1 >> right, dst++);
166 d0 = d1;
167 d1 = FB_READL(src++);
168 FB_WRITEL(d0 << left | d1 >> right, dst++);
169 d0 = d1;
170 n -= 4;
172 while (n--) {
173 d1 = FB_READL(src++);
174 d1 = fb_rev_pixels_in_long(d1, bswapmask);
175 d0 = d0 << left | d1 >> right;
176 d0 = fb_rev_pixels_in_long(d0, bswapmask);
177 FB_WRITEL(d0, dst++);
178 d0 = d1;
181 // Trailing bits
182 if (last) {
183 if (m <= right) {
184 // Single source word
185 d0 <<= left;
186 } else {
187 // 2 source words
188 d1 = FB_READL(src);
189 d1 = fb_rev_pixels_in_long(d1,
190 bswapmask);
191 d0 = d0<<left | d1>>right;
193 d0 = fb_rev_pixels_in_long(d0, bswapmask);
194 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
201 * Generic bitwise copy algorithm, operating backward
204 static void
205 bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src,
206 int src_idx, int bits, unsigned n, u32 bswapmask)
208 unsigned long first, last;
209 int shift;
211 dst += (n-1)/bits;
212 src += (n-1)/bits;
213 if ((n-1) % bits) {
214 dst_idx += (n-1) % bits;
215 dst += dst_idx >> (ffs(bits) - 1);
216 dst_idx &= bits - 1;
217 src_idx += (n-1) % bits;
218 src += src_idx >> (ffs(bits) - 1);
219 src_idx &= bits - 1;
222 shift = dst_idx-src_idx;
224 first = fb_shifted_pixels_mask_long(bits - 1 - dst_idx, bswapmask);
225 last = ~fb_shifted_pixels_mask_long(bits - 1 - ((dst_idx-n) % bits), bswapmask);
227 if (!shift) {
228 // Same alignment for source and dest
230 if ((unsigned long)dst_idx+1 >= n) {
231 // Single word
232 if (last)
233 first &= last;
234 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
235 } else {
236 // Multiple destination words
238 // Leading bits
239 if (first != ~0UL) {
240 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
241 dst--;
242 src--;
243 n -= dst_idx+1;
246 // Main chunk
247 n /= bits;
248 while (n >= 8) {
249 FB_WRITEL(FB_READL(src--), dst--);
250 FB_WRITEL(FB_READL(src--), dst--);
251 FB_WRITEL(FB_READL(src--), dst--);
252 FB_WRITEL(FB_READL(src--), dst--);
253 FB_WRITEL(FB_READL(src--), dst--);
254 FB_WRITEL(FB_READL(src--), dst--);
255 FB_WRITEL(FB_READL(src--), dst--);
256 FB_WRITEL(FB_READL(src--), dst--);
257 n -= 8;
259 while (n--)
260 FB_WRITEL(FB_READL(src--), dst--);
262 // Trailing bits
263 if (last)
264 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
266 } else {
267 // Different alignment for source and dest
268 unsigned long d0, d1;
269 int m;
271 int const left = -shift & (bits-1);
272 int const right = shift & (bits-1);
273 bswapmask &= shift;
275 if ((unsigned long)dst_idx+1 >= n) {
276 // Single destination word
277 if (last)
278 first &= last;
279 d0 = FB_READL(src);
280 if (shift < 0) {
281 // Single source word
282 d0 <<= left;
283 } else if (1+(unsigned long)src_idx >= n) {
284 // Single source word
285 d0 >>= right;
286 } else {
287 // 2 source words
288 d1 = FB_READL(src - 1);
289 d1 = fb_rev_pixels_in_long(d1, bswapmask);
290 d0 = d0>>right | d1<<left;
292 d0 = fb_rev_pixels_in_long(d0, bswapmask);
293 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
294 } else {
295 // Multiple destination words
296 /** We must always remember the last value read, because in case
297 SRC and DST overlap bitwise (e.g. when moving just one pixel in
298 1bpp), we always collect one full long for DST and that might
299 overlap with the current long from SRC. We store this value in
300 'd0'. */
302 d0 = FB_READL(src--);
303 d0 = fb_rev_pixels_in_long(d0, bswapmask);
304 // Leading bits
305 if (shift < 0) {
306 // Single source word
307 d1 = d0;
308 d0 <<= left;
309 } else {
310 // 2 source words
311 d1 = FB_READL(src--);
312 d1 = fb_rev_pixels_in_long(d1, bswapmask);
313 d0 = d0>>right | d1<<left;
315 d0 = fb_rev_pixels_in_long(d0, bswapmask);
316 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
317 d0 = d1;
318 dst--;
319 n -= dst_idx+1;
321 // Main chunk
322 m = n % bits;
323 n /= bits;
324 while ((n >= 4) && !bswapmask) {
325 d1 = FB_READL(src--);
326 FB_WRITEL(d0 >> right | d1 << left, dst--);
327 d0 = d1;
328 d1 = FB_READL(src--);
329 FB_WRITEL(d0 >> right | d1 << left, dst--);
330 d0 = d1;
331 d1 = FB_READL(src--);
332 FB_WRITEL(d0 >> right | d1 << left, dst--);
333 d0 = d1;
334 d1 = FB_READL(src--);
335 FB_WRITEL(d0 >> right | d1 << left, dst--);
336 d0 = d1;
337 n -= 4;
339 while (n--) {
340 d1 = FB_READL(src--);
341 d1 = fb_rev_pixels_in_long(d1, bswapmask);
342 d0 = d0 >> right | d1 << left;
343 d0 = fb_rev_pixels_in_long(d0, bswapmask);
344 FB_WRITEL(d0, dst--);
345 d0 = d1;
348 // Trailing bits
349 if (last) {
350 if (m <= left) {
351 // Single source word
352 d0 >>= right;
353 } else {
354 // 2 source words
355 d1 = FB_READL(src);
356 d1 = fb_rev_pixels_in_long(d1,
357 bswapmask);
358 d0 = d0>>right | d1<<left;
360 d0 = fb_rev_pixels_in_long(d0, bswapmask);
361 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
367 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
369 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
370 u32 height = area->height, width = area->width;
371 unsigned long const bits_per_line = p->fix.line_length*8u;
372 unsigned long __iomem *dst = NULL, *src = NULL;
373 int bits = BITS_PER_LONG, bytes = bits >> 3;
374 int dst_idx = 0, src_idx = 0, rev_copy = 0;
375 u32 bswapmask = fb_compute_bswapmask(p);
377 if (p->state != FBINFO_STATE_RUNNING)
378 return;
380 /* if the beginning of the target area might overlap with the end of
381 the source area, be have to copy the area reverse. */
382 if ((dy == sy && dx > sx) || (dy > sy)) {
383 dy += height;
384 sy += height;
385 rev_copy = 1;
388 // split the base of the framebuffer into a long-aligned address and the
389 // index of the first bit
390 dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
391 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
392 // add offset of source and target area
393 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
394 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
396 if (p->fbops->fb_sync)
397 p->fbops->fb_sync(p);
399 if (rev_copy) {
400 while (height--) {
401 dst_idx -= bits_per_line;
402 src_idx -= bits_per_line;
403 dst += dst_idx >> (ffs(bits) - 1);
404 dst_idx &= (bytes - 1);
405 src += src_idx >> (ffs(bits) - 1);
406 src_idx &= (bytes - 1);
407 bitcpy_rev(dst, dst_idx, src, src_idx, bits,
408 width*p->var.bits_per_pixel, bswapmask);
410 } else {
411 while (height--) {
412 dst += dst_idx >> (ffs(bits) - 1);
413 dst_idx &= (bytes - 1);
414 src += src_idx >> (ffs(bits) - 1);
415 src_idx &= (bytes - 1);
416 bitcpy(dst, dst_idx, src, src_idx, bits,
417 width*p->var.bits_per_pixel, bswapmask);
418 dst_idx += bits_per_line;
419 src_idx += bits_per_line;
424 EXPORT_SYMBOL(cfb_copyarea);
426 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
427 MODULE_DESCRIPTION("Generic software accelerated copyarea");
428 MODULE_LICENSE("GPL");