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