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 #include <linux/module.h>
26 #include <linux/kernel.h>
27 #include <linux/string.h>
28 #include <linux/fb.h>
29 #include <asm/types.h>
30 #include <asm/io.h>
31 #include "fb_draw.h"
32
33 #if BITS_PER_LONG == 32
34 # define FB_WRITEL fb_writel
35 # define FB_READL fb_readl
36 #else
37 # define FB_WRITEL fb_writeq
38 # define FB_READL fb_readq
39 #endif
40
41 /*
42 * Generic bitwise copy algorithm
43 */
44
45 static void
bitcpy(struct fb_info * p,unsigned long __iomem * dst,int dst_idx,const unsigned long __iomem * src,int src_idx,int bits,unsigned n,u32 bswapmask)46 bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
47 const unsigned long __iomem *src, int src_idx, int bits,
48 unsigned n, u32 bswapmask)
49 {
50 unsigned long first, last;
51 int const shift = dst_idx-src_idx;
52 int left, right;
53
54 first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
55 last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
56
57 if (!shift) {
58 // Same alignment for source and dest
59
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
67
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;
74 }
75
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;
88 }
89 while (n--)
90 FB_WRITEL(FB_READL(src++), dst++);
91
92 // Trailing bits
93 if (last)
94 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
95 }
96 } else {
97 /* Different alignment for source and dest */
98 unsigned long d0, d1;
99 int m;
100
101 right = shift & (bits - 1);
102 left = -shift & (bits - 1);
103 bswapmask &= shift;
104
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;
122 }
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);
145
146 d0 = d0<<left | d1>>right;
147 dst++;
148 n -= bits - dst_idx;
149 }
150 d0 = fb_rev_pixels_in_long(d0, bswapmask);
151 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
152 d0 = d1;
153
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;
171 }
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;
179 }
180
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;
192 }
193 d0 = fb_rev_pixels_in_long(d0, bswapmask);
194 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
195 }
196 }
197 }
198 }
199
200 /*
201 * Generic bitwise copy algorithm, operating backward
202 */
203
204 static void
bitcpy_rev(struct fb_info * p,unsigned long __iomem * dst,int dst_idx,const unsigned long __iomem * src,int src_idx,int bits,unsigned n,u32 bswapmask)205 bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
206 const unsigned long __iomem *src, int src_idx, int bits,
207 unsigned n, u32 bswapmask)
208 {
209 unsigned long first, last;
210 int shift;
211
212 dst += (n-1)/bits;
213 src += (n-1)/bits;
214 if ((n-1) % bits) {
215 dst_idx += (n-1) % bits;
216 dst += dst_idx >> (ffs(bits) - 1);
217 dst_idx &= bits - 1;
218 src_idx += (n-1) % bits;
219 src += src_idx >> (ffs(bits) - 1);
220 src_idx &= bits - 1;
221 }
222
223 shift = dst_idx-src_idx;
224
225 first = fb_shifted_pixels_mask_long(p, bits - 1 - dst_idx, bswapmask);
226 last = ~fb_shifted_pixels_mask_long(p, bits - 1 - ((dst_idx-n) % bits),
227 bswapmask);
228
229 if (!shift) {
230 // Same alignment for source and dest
231
232 if ((unsigned long)dst_idx+1 >= n) {
233 // Single word
234 if (last)
235 first &= last;
236 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
237 } else {
238 // Multiple destination words
239
240 // Leading bits
241 if (first != ~0UL) {
242 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
243 dst--;
244 src--;
245 n -= dst_idx+1;
246 }
247
248 // Main chunk
249 n /= bits;
250 while (n >= 8) {
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 FB_WRITEL(FB_READL(src--), dst--);
258 FB_WRITEL(FB_READL(src--), dst--);
259 n -= 8;
260 }
261 while (n--)
262 FB_WRITEL(FB_READL(src--), dst--);
263
264 // Trailing bits
265 if (last)
266 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
267 }
268 } else {
269 // Different alignment for source and dest
270 unsigned long d0, d1;
271 int m;
272
273 int const left = -shift & (bits-1);
274 int const right = shift & (bits-1);
275 bswapmask &= shift;
276
277 if ((unsigned long)dst_idx+1 >= n) {
278 // Single destination word
279 if (last)
280 first &= last;
281 d0 = FB_READL(src);
282 if (shift < 0) {
283 // Single source word
284 d0 <<= left;
285 } else if (1+(unsigned long)src_idx >= n) {
286 // Single source word
287 d0 >>= right;
288 } else {
289 // 2 source words
290 d1 = FB_READL(src - 1);
291 d1 = fb_rev_pixels_in_long(d1, bswapmask);
292 d0 = d0>>right | d1<<left;
293 }
294 d0 = fb_rev_pixels_in_long(d0, bswapmask);
295 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
296 } else {
297 // Multiple destination words
298 /** We must always remember the last value read, because in case
299 SRC and DST overlap bitwise (e.g. when moving just one pixel in
300 1bpp), we always collect one full long for DST and that might
301 overlap with the current long from SRC. We store this value in
302 'd0'. */
303
304 d0 = FB_READL(src--);
305 d0 = fb_rev_pixels_in_long(d0, bswapmask);
306 // Leading bits
307 if (shift < 0) {
308 // Single source word
309 d1 = d0;
310 d0 <<= left;
311 } else {
312 // 2 source words
313 d1 = FB_READL(src--);
314 d1 = fb_rev_pixels_in_long(d1, bswapmask);
315 d0 = d0>>right | d1<<left;
316 }
317 d0 = fb_rev_pixels_in_long(d0, bswapmask);
318 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
319 d0 = d1;
320 dst--;
321 n -= dst_idx+1;
322
323 // Main chunk
324 m = n % bits;
325 n /= bits;
326 while ((n >= 4) && !bswapmask) {
327 d1 = FB_READL(src--);
328 FB_WRITEL(d0 >> right | d1 << left, dst--);
329 d0 = d1;
330 d1 = FB_READL(src--);
331 FB_WRITEL(d0 >> right | d1 << left, dst--);
332 d0 = d1;
333 d1 = FB_READL(src--);
334 FB_WRITEL(d0 >> right | d1 << left, dst--);
335 d0 = d1;
336 d1 = FB_READL(src--);
337 FB_WRITEL(d0 >> right | d1 << left, dst--);
338 d0 = d1;
339 n -= 4;
340 }
341 while (n--) {
342 d1 = FB_READL(src--);
343 d1 = fb_rev_pixels_in_long(d1, bswapmask);
344 d0 = d0 >> right | d1 << left;
345 d0 = fb_rev_pixels_in_long(d0, bswapmask);
346 FB_WRITEL(d0, dst--);
347 d0 = d1;
348 }
349
350 // Trailing bits
351 if (last) {
352 if (m <= left) {
353 // Single source word
354 d0 >>= right;
355 } else {
356 // 2 source words
357 d1 = FB_READL(src);
358 d1 = fb_rev_pixels_in_long(d1,
359 bswapmask);
360 d0 = d0>>right | d1<<left;
361 }
362 d0 = fb_rev_pixels_in_long(d0, bswapmask);
363 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
364 }
365 }
366 }
367 }
368
cfb_copyarea(struct fb_info * p,const struct fb_copyarea * area)369 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
370 {
371 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
372 u32 height = area->height, width = area->width;
373 unsigned long const bits_per_line = p->fix.line_length*8u;
374 unsigned long __iomem *dst = NULL, *src = NULL;
375 int bits = BITS_PER_LONG, bytes = bits >> 3;
376 int dst_idx = 0, src_idx = 0, rev_copy = 0;
377 u32 bswapmask = fb_compute_bswapmask(p);
378
379 if (p->state != FBINFO_STATE_RUNNING)
380 return;
381
382 /* if the beginning of the target area might overlap with the end of
383 the source area, be have to copy the area reverse. */
384 if ((dy == sy && dx > sx) || (dy > sy)) {
385 dy += height;
386 sy += height;
387 rev_copy = 1;
388 }
389
390 // split the base of the framebuffer into a long-aligned address and the
391 // index of the first bit
392 dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
393 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
394 // add offset of source and target area
395 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
396 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
397
398 if (p->fbops->fb_sync)
399 p->fbops->fb_sync(p);
400
401 if (rev_copy) {
402 while (height--) {
403 dst_idx -= bits_per_line;
404 src_idx -= bits_per_line;
405 dst += dst_idx >> (ffs(bits) - 1);
406 dst_idx &= (bytes - 1);
407 src += src_idx >> (ffs(bits) - 1);
408 src_idx &= (bytes - 1);
409 bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
410 width*p->var.bits_per_pixel, bswapmask);
411 }
412 } else {
413 while (height--) {
414 dst += dst_idx >> (ffs(bits) - 1);
415 dst_idx &= (bytes - 1);
416 src += src_idx >> (ffs(bits) - 1);
417 src_idx &= (bytes - 1);
418 bitcpy(p, dst, dst_idx, src, src_idx, bits,
419 width*p->var.bits_per_pixel, bswapmask);
420 dst_idx += bits_per_line;
421 src_idx += bits_per_line;
422 }
423 }
424 }
425
426 EXPORT_SYMBOL(cfb_copyarea);
427
428 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
429 MODULE_DESCRIPTION("Generic software accelerated copyarea");
430 MODULE_LICENSE("GPL");
431
432