blob: e3488932c7b34debf3680413103f6c6b1a0551a4 [file] [log] [blame]
Antonino A. Daplas68648ed2007-05-08 00:38:57 -07001/*
2 * Generic Bit Block Transfer for frame buffers located in system RAM with
3 * packed pixels of any depth.
4 *
5 * Based almost entirely from cfbcopyarea.c (which is based almost entirely
6 * on Geert Uytterhoeven's copyarea routine)
7 *
8 * Copyright (C) 2007 Antonino Daplas <adaplas@pol.net>
9 *
10 * This file is subject to the terms and conditions of the GNU General Public
11 * License. See the file COPYING in the main directory of this archive for
12 * more details.
13 *
14 */
15#include <linux/module.h>
16#include <linux/kernel.h>
17#include <linux/string.h>
18#include <linux/fb.h>
19#include <linux/slab.h>
20#include <asm/types.h>
21#include <asm/io.h>
22
23 /*
24 * Compose two values, using a bitmask as decision value
25 * This is equivalent to (a & mask) | (b & ~mask)
26 */
27
28static inline unsigned long
29comp(unsigned long a, unsigned long b, unsigned long mask)
30{
31 return ((a ^ b) & mask) ^ b;
32}
33
34 /*
35 * Generic bitwise copy algorithm
36 */
37
38static void
39bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
40 int src_idx, int bits, unsigned n)
41{
42 unsigned long first, last;
43 int const shift = dst_idx-src_idx;
44 int left, right;
45
46 first = FB_SHIFT_HIGH(~0UL, dst_idx);
47 last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));
48
49 if (!shift) {
50 /* Same alignment for source and dest */
51 if (dst_idx+n <= bits) {
52 /* Single word */
53 if (last)
54 first &= last;
55 *dst = comp(*src, *dst, first);
56 } else {
57 /* Multiple destination words */
58 /* Leading bits */
59 if (first != ~0UL) {
60 *dst = comp(*src, *dst, first);
61 dst++;
62 src++;
63 n -= bits - dst_idx;
64 }
65
66 /* Main chunk */
67 n /= bits;
68 while (n >= 8) {
69 *dst++ = *src++;
70 *dst++ = *src++;
71 *dst++ = *src++;
72 *dst++ = *src++;
73 *dst++ = *src++;
74 *dst++ = *src++;
75 *dst++ = *src++;
76 *dst++ = *src++;
77 n -= 8;
78 }
79 while (n--)
80 *dst++ = *src++;
81
82 /* Trailing bits */
83 if (last)
84 *dst = comp(*src, *dst, last);
85 }
86 } else {
87 unsigned long d0, d1;
88 int m;
89
90 /* Different alignment for source and dest */
91 right = shift & (bits - 1);
92 left = -shift & (bits - 1);
93
94 if (dst_idx+n <= bits) {
95 /* Single destination word */
96 if (last)
97 first &= last;
98 if (shift > 0) {
99 /* Single source word */
100 *dst = comp(*src >> right, *dst, first);
101 } else if (src_idx+n <= bits) {
102 /* Single source word */
103 *dst = comp(*src << left, *dst, first);
104 } else {
105 /* 2 source words */
106 d0 = *src++;
107 d1 = *src;
108 *dst = comp(d0 << left | d1 >> right, *dst,
109 first);
110 }
111 } else {
112 /* Multiple destination words */
113 /** We must always remember the last value read,
114 because in case SRC and DST overlap bitwise (e.g.
115 when moving just one pixel in 1bpp), we always
116 collect one full long for DST and that might
117 overlap with the current long from SRC. We store
118 this value in 'd0'. */
119 d0 = *src++;
120 /* Leading bits */
121 if (shift > 0) {
122 /* Single source word */
123 *dst = comp(d0 >> right, *dst, first);
124 dst++;
125 n -= bits - dst_idx;
126 } else {
127 /* 2 source words */
128 d1 = *src++;
129 *dst = comp(d0 << left | *dst >> right, *dst, first);
130 d0 = d1;
131 dst++;
132 n -= bits - dst_idx;
133 }
134
135 /* Main chunk */
136 m = n % bits;
137 n /= bits;
138 while (n >= 4) {
139 d1 = *src++;
140 *dst++ = d0 << left | d1 >> right;
141 d0 = d1;
142 d1 = *src++;
143 *dst++ = d0 << left | d1 >> right;
144 d0 = d1;
145 d1 = *src++;
146 *dst++ = d0 << left | d1 >> right;
147 d0 = d1;
148 d1 = *src++;
149 *dst++ = d0 << left | d1 >> right;
150 d0 = d1;
151 n -= 4;
152 }
153 while (n--) {
154 d1 = *src++;
155 *dst++ = d0 << left | d1 >> right;
156 d0 = d1;
157 }
158
159 /* Trailing bits */
160 if (last) {
161 if (m <= right) {
162 /* Single source word */
163 *dst = comp(d0 << left, *dst, last);
164 } else {
165 /* 2 source words */
166 d1 = *src;
167 *dst = comp(d0 << left | d1 >> right,
168 *dst, last);
169 }
170 }
171 }
172 }
173}
174
175 /*
176 * Generic bitwise copy algorithm, operating backward
177 */
178
179static void
180bitcpy_rev(unsigned long *dst, int dst_idx, const unsigned long *src,
181 int src_idx, int bits, unsigned n)
182{
183 unsigned long first, last;
184 int shift;
185
186 dst += (n-1)/bits;
187 src += (n-1)/bits;
188 if ((n-1) % bits) {
189 dst_idx += (n-1) % bits;
190 dst += dst_idx >> (ffs(bits) - 1);
191 dst_idx &= bits - 1;
192 src_idx += (n-1) % bits;
193 src += src_idx >> (ffs(bits) - 1);
194 src_idx &= bits - 1;
195 }
196
197 shift = dst_idx-src_idx;
198
199 first = FB_SHIFT_LOW(~0UL, bits - 1 - dst_idx);
200 last = ~(FB_SHIFT_LOW(~0UL, bits - 1 - ((dst_idx-n) % bits)));
201
202 if (!shift) {
203 /* Same alignment for source and dest */
204 if ((unsigned long)dst_idx+1 >= n) {
205 /* Single word */
206 if (last)
207 first &= last;
208 *dst = comp(*src, *dst, first);
209 } else {
210 /* Multiple destination words */
211
212 /* Leading bits */
213 if (first != ~0UL) {
214 *dst = comp(*src, *dst, first);
215 dst--;
216 src--;
217 n -= dst_idx+1;
218 }
219
220 /* Main chunk */
221 n /= bits;
222 while (n >= 8) {
223 *dst-- = *src--;
224 *dst-- = *src--;
225 *dst-- = *src--;
226 *dst-- = *src--;
227 *dst-- = *src--;
228 *dst-- = *src--;
229 *dst-- = *src--;
230 *dst-- = *src--;
231 n -= 8;
232 }
233 while (n--)
234 *dst-- = *src--;
235 /* Trailing bits */
236 if (last)
237 *dst = comp(*src, *dst, last);
238 }
239 } else {
240 /* Different alignment for source and dest */
241
242 int const left = -shift & (bits-1);
243 int const right = shift & (bits-1);
244
245 if ((unsigned long)dst_idx+1 >= n) {
246 /* Single destination word */
247 if (last)
248 first &= last;
249 if (shift < 0) {
250 /* Single source word */
251 *dst = comp(*src << left, *dst, first);
252 } else if (1+(unsigned long)src_idx >= n) {
253 /* Single source word */
254 *dst = comp(*src >> right, *dst, first);
255 } else {
256 /* 2 source words */
257 *dst = comp(*src >> right | *(src-1) << left,
258 *dst, first);
259 }
260 } else {
261 /* Multiple destination words */
262 /** We must always remember the last value read,
263 because in case SRC and DST overlap bitwise (e.g.
264 when moving just one pixel in 1bpp), we always
265 collect one full long for DST and that might
266 overlap with the current long from SRC. We store
267 this value in 'd0'. */
268 unsigned long d0, d1;
269 int m;
270
271 d0 = *src--;
272 /* Leading bits */
273 if (shift < 0) {
274 /* Single source word */
275 *dst = comp(d0 << left, *dst, first);
276 } else {
277 /* 2 source words */
278 d1 = *src--;
279 *dst = comp(d0 >> right | d1 << left, *dst,
280 first);
281 d0 = d1;
282 }
283 dst--;
284 n -= dst_idx+1;
285
286 /* Main chunk */
287 m = n % bits;
288 n /= bits;
289 while (n >= 4) {
290 d1 = *src--;
291 *dst-- = d0 >> right | d1 << left;
292 d0 = d1;
293 d1 = *src--;
294 *dst-- = d0 >> right | d1 << left;
295 d0 = d1;
296 d1 = *src--;
297 *dst-- = d0 >> right | d1 << left;
298 d0 = d1;
299 d1 = *src--;
300 *dst-- = d0 >> right | d1 << left;
301 d0 = d1;
302 n -= 4;
303 }
304 while (n--) {
305 d1 = *src--;
306 *dst-- = d0 >> right | d1 << left;
307 d0 = d1;
308 }
309
310 /* Trailing bits */
311 if (last) {
312 if (m <= left) {
313 /* Single source word */
314 *dst = comp(d0 >> right, *dst, last);
315 } else {
316 /* 2 source words */
317 d1 = *src;
318 *dst = comp(d0 >> right | d1 << left,
319 *dst, last);
320 }
321 }
322 }
323 }
324}
325
326void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area)
327{
328 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
329 u32 height = area->height, width = area->width;
330 unsigned long const bits_per_line = p->fix.line_length*8u;
331 unsigned long *dst = NULL, *src = NULL;
332 int bits = BITS_PER_LONG, bytes = bits >> 3;
333 int dst_idx = 0, src_idx = 0, rev_copy = 0;
334
335 if (p->state != FBINFO_STATE_RUNNING)
336 return;
337
338 /* if the beginning of the target area might overlap with the end of
339 the source area, be have to copy the area reverse. */
340 if ((dy == sy && dx > sx) || (dy > sy)) {
341 dy += height;
342 sy += height;
343 rev_copy = 1;
344 }
345
346 /* split the base of the framebuffer into a long-aligned address and
347 the index of the first bit */
348 dst = src = (unsigned long *)((unsigned long)p->screen_base &
349 ~(bytes-1));
350 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
351 /* add offset of source and target area */
352 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
353 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
354
355 if (p->fbops->fb_sync)
356 p->fbops->fb_sync(p);
357
358 if (rev_copy) {
359 while (height--) {
360 dst_idx -= bits_per_line;
361 src_idx -= bits_per_line;
362 dst += dst_idx >> (ffs(bits) - 1);
363 dst_idx &= (bytes - 1);
364 src += src_idx >> (ffs(bits) - 1);
365 src_idx &= (bytes - 1);
366 bitcpy_rev(dst, dst_idx, src, src_idx, bits,
367 width*p->var.bits_per_pixel);
368 }
369 } else {
370 while (height--) {
371 dst += dst_idx >> (ffs(bits) - 1);
372 dst_idx &= (bytes - 1);
373 src += src_idx >> (ffs(bits) - 1);
374 src_idx &= (bytes - 1);
375 bitcpy(dst, dst_idx, src, src_idx, bits,
376 width*p->var.bits_per_pixel);
377 dst_idx += bits_per_line;
378 src_idx += bits_per_line;
379 }
380 }
381}
382
383EXPORT_SYMBOL(sys_copyarea);
384
385MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
386MODULE_DESCRIPTION("Generic copyarea (sys-to-sys)");
387MODULE_LICENSE("GPL");
388