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