1 /*
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 <asm/types.h>
20 #include <asm/io.h>
21 #include "fb_draw.h"
22
23 /*
24 * Generic bitwise copy algorithm
25 */
26
27 static void
bitcpy(struct fb_info * p,unsigned long * dst,unsigned dst_idx,const unsigned long * src,unsigned src_idx,int bits,unsigned n)28 bitcpy(struct fb_info *p, unsigned long *dst, unsigned dst_idx,
29 const unsigned long *src, unsigned src_idx, int bits, unsigned n)
30 {
31 unsigned long first, last;
32 int const shift = dst_idx-src_idx;
33 int left, right;
34
35 first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
36 last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
37
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 << left, *dst, first);
90 } else if (src_idx+n <= bits) {
91 /* Single source word */
92 *dst = comp(*src >> right, *dst, first);
93 } else {
94 /* 2 source words */
95 d0 = *src++;
96 d1 = *src;
97 *dst = comp(d0 >> right | d1 << left, *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 << left, *dst, first);
113 dst++;
114 n -= bits - dst_idx;
115 } else {
116 /* 2 source words */
117 d1 = *src++;
118 *dst = comp(d0 >> right | d1 << left, *dst,
119 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 >> right | d1 << left;
131 d0 = d1;
132 d1 = *src++;
133 *dst++ = d0 >> right | d1 << left;
134 d0 = d1;
135 d1 = *src++;
136 *dst++ = d0 >> right | d1 << left;
137 d0 = d1;
138 d1 = *src++;
139 *dst++ = d0 >> right | d1 << left;
140 d0 = d1;
141 n -= 4;
142 }
143 while (n--) {
144 d1 = *src++;
145 *dst++ = d0 >> right | d1 << left;
146 d0 = d1;
147 }
148
149 /* Trailing bits */
150 if (m) {
151 if (m <= bits - right) {
152 /* Single source word */
153 d0 >>= right;
154 } else {
155 /* 2 source words */
156 d1 = *src;
157 d0 = d0 >> right | d1 << left;
158 }
159 *dst = comp(d0, *dst, last);
160 }
161 }
162 }
163 }
164
165 /*
166 * Generic bitwise copy algorithm, operating backward
167 */
168
169 static void
bitcpy_rev(struct fb_info * p,unsigned long * dst,unsigned dst_idx,const unsigned long * src,unsigned src_idx,unsigned bits,unsigned n)170 bitcpy_rev(struct fb_info *p, unsigned long *dst, unsigned dst_idx,
171 const unsigned long *src, unsigned src_idx, unsigned bits,
172 unsigned n)
173 {
174 unsigned long first, last;
175 int shift;
176
177 dst += (dst_idx + n - 1) / bits;
178 src += (src_idx + n - 1) / bits;
179 dst_idx = (dst_idx + n - 1) % bits;
180 src_idx = (src_idx + n - 1) % bits;
181
182 shift = dst_idx-src_idx;
183
184 first = ~FB_SHIFT_HIGH(p, ~0UL, (dst_idx + 1) % bits);
185 last = FB_SHIFT_HIGH(p, ~0UL, (bits + dst_idx + 1 - n) % bits);
186
187 if (!shift) {
188 /* Same alignment for source and dest */
189 if ((unsigned long)dst_idx+1 >= n) {
190 /* Single word */
191 if (first)
192 last &= first;
193 *dst = comp(*src, *dst, last);
194 } else {
195 /* Multiple destination words */
196
197 /* Leading bits */
198 if (first) {
199 *dst = comp(*src, *dst, first);
200 dst--;
201 src--;
202 n -= dst_idx+1;
203 }
204
205 /* Main chunk */
206 n /= bits;
207 while (n >= 8) {
208 *dst-- = *src--;
209 *dst-- = *src--;
210 *dst-- = *src--;
211 *dst-- = *src--;
212 *dst-- = *src--;
213 *dst-- = *src--;
214 *dst-- = *src--;
215 *dst-- = *src--;
216 n -= 8;
217 }
218 while (n--)
219 *dst-- = *src--;
220 /* Trailing bits */
221 if (last != -1UL)
222 *dst = comp(*src, *dst, last);
223 }
224 } else {
225 /* Different alignment for source and dest */
226
227 int const left = shift & (bits-1);
228 int const right = -shift & (bits-1);
229
230 if ((unsigned long)dst_idx+1 >= n) {
231 /* Single destination word */
232 if (first)
233 last &= first;
234 if (shift < 0) {
235 /* Single source word */
236 *dst = comp(*src >> right, *dst, last);
237 } else if (1+(unsigned long)src_idx >= n) {
238 /* Single source word */
239 *dst = comp(*src << left, *dst, last);
240 } else {
241 /* 2 source words */
242 *dst = comp(*src << left | *(src-1) >> right,
243 *dst, last);
244 }
245 } else {
246 /* Multiple destination words */
247 /** We must always remember the last value read,
248 because in case SRC and DST overlap bitwise (e.g.
249 when moving just one pixel in 1bpp), we always
250 collect one full long for DST and that might
251 overlap with the current long from SRC. We store
252 this value in 'd0'. */
253 unsigned long d0, d1;
254 int m;
255
256 d0 = *src--;
257 /* Leading bits */
258 if (shift < 0) {
259 /* Single source word */
260 d1 = d0;
261 d0 >>= right;
262 } else {
263 /* 2 source words */
264 d1 = *src--;
265 d0 = d0 << left | d1 >> right;
266 }
267 if (!first)
268 *dst = d0;
269 else
270 *dst = comp(d0, *dst, first);
271 d0 = d1;
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 << left | d1 >> right;
281 d0 = d1;
282 d1 = *src--;
283 *dst-- = d0 << left | d1 >> right;
284 d0 = d1;
285 d1 = *src--;
286 *dst-- = d0 << left | d1 >> right;
287 d0 = d1;
288 d1 = *src--;
289 *dst-- = d0 << left | d1 >> right;
290 d0 = d1;
291 n -= 4;
292 }
293 while (n--) {
294 d1 = *src--;
295 *dst-- = d0 << left | d1 >> right;
296 d0 = d1;
297 }
298
299 /* Trailing bits */
300 if (m) {
301 if (m <= bits - left) {
302 /* Single source word */
303 d0 <<= left;
304 } else {
305 /* 2 source words */
306 d1 = *src;
307 d0 = d0 << left | d1 >> right;
308 }
309 *dst = comp(d0, *dst, last);
310 }
311 }
312 }
313 }
314
sys_copyarea(struct fb_info * p,const struct fb_copyarea * area)315 void 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 int const bits_per_line = p->fix.line_length * 8u;
320 unsigned long *base = NULL;
321 int bits = BITS_PER_LONG, bytes = bits >> 3;
322 unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
323
324 if (p->state != FBINFO_STATE_RUNNING)
325 return;
326
327 if (!(p->flags & FBINFO_VIRTFB))
328 fb_warn_once(p, "Framebuffer is not in virtual address space.");
329
330 /* if the beginning of the target area might overlap with the end of
331 the source area, be have to copy the area reverse. */
332 if ((dy == sy && dx > sx) || (dy > sy)) {
333 dy += height;
334 sy += height;
335 rev_copy = 1;
336 }
337
338 /* split the base of the framebuffer into a long-aligned address and
339 the index of the first bit */
340 base = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
341 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
342 /* add offset of source and target area */
343 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
344 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
345
346 if (p->fbops->fb_sync)
347 p->fbops->fb_sync(p);
348
349 if (rev_copy) {
350 while (height--) {
351 dst_idx -= bits_per_line;
352 src_idx -= bits_per_line;
353 bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
354 base + (src_idx / bits), src_idx % bits, bits,
355 width*p->var.bits_per_pixel);
356 }
357 } else {
358 while (height--) {
359 bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
360 base + (src_idx / bits), src_idx % bits, bits,
361 width*p->var.bits_per_pixel);
362 dst_idx += bits_per_line;
363 src_idx += bits_per_line;
364 }
365 }
366 }
367
368 EXPORT_SYMBOL(sys_copyarea);
369
370 MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
371 MODULE_DESCRIPTION("Generic copyarea (sys-to-sys)");
372 MODULE_LICENSE("GPL");
373
374