Line data Source code
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
28 0 : 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 0 : int const shift = dst_idx-src_idx;
33 : int left, right;
34 :
35 0 : first = FB_SHIFT_HIGH(p, ~0UL, dst_idx);
36 0 : last = ~(FB_SHIFT_HIGH(p, ~0UL, (dst_idx+n) % bits));
37 :
38 0 : if (!shift) {
39 : /* Same alignment for source and dest */
40 0 : if (dst_idx+n <= bits) {
41 : /* Single word */
42 0 : if (last)
43 0 : first &= last;
44 0 : *dst = comp(*src, *dst, first);
45 : } else {
46 : /* Multiple destination words */
47 : /* Leading bits */
48 0 : if (first != ~0UL) {
49 0 : *dst = comp(*src, *dst, first);
50 0 : dst++;
51 0 : src++;
52 0 : n -= bits - dst_idx;
53 : }
54 :
55 : /* Main chunk */
56 0 : n /= bits;
57 0 : while (n >= 8) {
58 0 : *dst++ = *src++;
59 0 : *dst++ = *src++;
60 0 : *dst++ = *src++;
61 0 : *dst++ = *src++;
62 0 : *dst++ = *src++;
63 0 : *dst++ = *src++;
64 0 : *dst++ = *src++;
65 0 : *dst++ = *src++;
66 0 : n -= 8;
67 : }
68 0 : while (n--)
69 0 : *dst++ = *src++;
70 :
71 : /* Trailing bits */
72 0 : if (last)
73 0 : *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 0 : right = shift & (bits - 1);
81 0 : left = -shift & (bits - 1);
82 :
83 0 : if (dst_idx+n <= bits) {
84 : /* Single destination word */
85 0 : if (last)
86 0 : first &= last;
87 0 : if (shift > 0) {
88 : /* Single source word */
89 0 : *dst = comp(*src << left, *dst, first);
90 0 : } else if (src_idx+n <= bits) {
91 : /* Single source word */
92 0 : *dst = comp(*src >> right, *dst, first);
93 : } else {
94 : /* 2 source words */
95 0 : d0 = *src++;
96 0 : d1 = *src;
97 0 : *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 0 : d0 = *src++;
109 : /* Leading bits */
110 0 : if (shift > 0) {
111 : /* Single source word */
112 0 : *dst = comp(d0 << left, *dst, first);
113 0 : dst++;
114 0 : n -= bits - dst_idx;
115 : } else {
116 : /* 2 source words */
117 0 : d1 = *src++;
118 0 : *dst = comp(d0 >> right | d1 << left, *dst,
119 : first);
120 0 : d0 = d1;
121 0 : dst++;
122 0 : n -= bits - dst_idx;
123 : }
124 :
125 : /* Main chunk */
126 0 : m = n % bits;
127 0 : n /= bits;
128 0 : while (n >= 4) {
129 0 : d1 = *src++;
130 0 : *dst++ = d0 >> right | d1 << left;
131 0 : d0 = d1;
132 0 : d1 = *src++;
133 0 : *dst++ = d0 >> right | d1 << left;
134 0 : d0 = d1;
135 0 : d1 = *src++;
136 0 : *dst++ = d0 >> right | d1 << left;
137 0 : d0 = d1;
138 0 : d1 = *src++;
139 0 : *dst++ = d0 >> right | d1 << left;
140 0 : d0 = d1;
141 0 : n -= 4;
142 : }
143 0 : while (n--) {
144 0 : d1 = *src++;
145 0 : *dst++ = d0 >> right | d1 << left;
146 0 : d0 = d1;
147 : }
148 :
149 : /* Trailing bits */
150 0 : if (m) {
151 0 : if (m <= bits - right) {
152 : /* Single source word */
153 0 : d0 >>= right;
154 : } else {
155 : /* 2 source words */
156 0 : d1 = *src;
157 0 : d0 = d0 >> right | d1 << left;
158 : }
159 0 : *dst = comp(d0, *dst, last);
160 : }
161 : }
162 : }
163 0 : }
164 :
165 : /*
166 : * Generic bitwise copy algorithm, operating backward
167 : */
168 :
169 : static void
170 0 : 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 0 : dst += (dst_idx + n - 1) / bits;
178 0 : src += (src_idx + n - 1) / bits;
179 0 : dst_idx = (dst_idx + n - 1) % bits;
180 0 : src_idx = (src_idx + n - 1) % bits;
181 :
182 0 : shift = dst_idx-src_idx;
183 :
184 0 : first = ~FB_SHIFT_HIGH(p, ~0UL, (dst_idx + 1) % bits);
185 0 : last = FB_SHIFT_HIGH(p, ~0UL, (bits + dst_idx + 1 - n) % bits);
186 :
187 0 : if (!shift) {
188 : /* Same alignment for source and dest */
189 0 : if ((unsigned long)dst_idx+1 >= n) {
190 : /* Single word */
191 0 : if (first)
192 0 : last &= first;
193 0 : *dst = comp(*src, *dst, last);
194 : } else {
195 : /* Multiple destination words */
196 :
197 : /* Leading bits */
198 0 : if (first) {
199 0 : *dst = comp(*src, *dst, first);
200 0 : dst--;
201 0 : src--;
202 0 : n -= dst_idx+1;
203 : }
204 :
205 : /* Main chunk */
206 0 : n /= bits;
207 0 : while (n >= 8) {
208 0 : *dst-- = *src--;
209 0 : *dst-- = *src--;
210 0 : *dst-- = *src--;
211 0 : *dst-- = *src--;
212 0 : *dst-- = *src--;
213 0 : *dst-- = *src--;
214 0 : *dst-- = *src--;
215 0 : *dst-- = *src--;
216 0 : n -= 8;
217 : }
218 0 : while (n--)
219 0 : *dst-- = *src--;
220 : /* Trailing bits */
221 0 : if (last != -1UL)
222 0 : *dst = comp(*src, *dst, last);
223 : }
224 : } else {
225 : /* Different alignment for source and dest */
226 :
227 0 : int const left = shift & (bits-1);
228 0 : int const right = -shift & (bits-1);
229 :
230 0 : if ((unsigned long)dst_idx+1 >= n) {
231 : /* Single destination word */
232 0 : if (first)
233 0 : last &= first;
234 0 : if (shift < 0) {
235 : /* Single source word */
236 0 : *dst = comp(*src >> right, *dst, last);
237 0 : } else if (1+(unsigned long)src_idx >= n) {
238 : /* Single source word */
239 0 : *dst = comp(*src << left, *dst, last);
240 : } else {
241 : /* 2 source words */
242 0 : *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 0 : d0 = *src--;
257 : /* Leading bits */
258 0 : if (shift < 0) {
259 : /* Single source word */
260 0 : d1 = d0;
261 0 : d0 >>= right;
262 : } else {
263 : /* 2 source words */
264 0 : d1 = *src--;
265 0 : d0 = d0 << left | d1 >> right;
266 : }
267 0 : if (!first)
268 0 : *dst = d0;
269 : else
270 0 : *dst = comp(d0, *dst, first);
271 0 : d0 = d1;
272 0 : dst--;
273 0 : n -= dst_idx+1;
274 :
275 : /* Main chunk */
276 0 : m = n % bits;
277 0 : n /= bits;
278 0 : while (n >= 4) {
279 0 : d1 = *src--;
280 0 : *dst-- = d0 << left | d1 >> right;
281 0 : d0 = d1;
282 0 : d1 = *src--;
283 0 : *dst-- = d0 << left | d1 >> right;
284 0 : d0 = d1;
285 0 : d1 = *src--;
286 0 : *dst-- = d0 << left | d1 >> right;
287 0 : d0 = d1;
288 0 : d1 = *src--;
289 0 : *dst-- = d0 << left | d1 >> right;
290 0 : d0 = d1;
291 0 : n -= 4;
292 : }
293 0 : while (n--) {
294 0 : d1 = *src--;
295 0 : *dst-- = d0 << left | d1 >> right;
296 0 : d0 = d1;
297 : }
298 :
299 : /* Trailing bits */
300 0 : if (m) {
301 0 : if (m <= bits - left) {
302 : /* Single source word */
303 0 : d0 <<= left;
304 : } else {
305 : /* 2 source words */
306 0 : d1 = *src;
307 0 : d0 = d0 << left | d1 >> right;
308 : }
309 0 : *dst = comp(d0, *dst, last);
310 : }
311 : }
312 : }
313 0 : }
314 :
315 0 : void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area)
316 : {
317 0 : u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
318 0 : u32 height = area->height, width = area->width;
319 0 : unsigned long const bits_per_line = p->fix.line_length*8u;
320 0 : unsigned long *base = NULL;
321 0 : int bits = BITS_PER_LONG, bytes = bits >> 3;
322 0 : unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
323 :
324 0 : 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 0 : if ((dy == sy && dx > sx) || (dy > sy)) {
330 0 : dy += height;
331 0 : sy += height;
332 0 : 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 0 : base = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1));
338 0 : dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
339 : /* add offset of source and target area */
340 0 : dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
341 0 : src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
342 :
343 0 : if (p->fbops->fb_sync)
344 0 : p->fbops->fb_sync(p);
345 :
346 0 : if (rev_copy) {
347 0 : while (height--) {
348 0 : dst_idx -= bits_per_line;
349 0 : src_idx -= bits_per_line;
350 0 : bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
351 0 : base + (src_idx / bits), src_idx % bits, bits,
352 0 : width*p->var.bits_per_pixel);
353 : }
354 : } else {
355 0 : while (height--) {
356 0 : bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
357 0 : base + (src_idx / bits), src_idx % bits, bits,
358 0 : width*p->var.bits_per_pixel);
359 0 : dst_idx += bits_per_line;
360 0 : src_idx += bits_per_line;
361 : }
362 : }
363 : }
364 :
365 : EXPORT_SYMBOL(sys_copyarea);
366 :
367 : MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
368 : MODULE_DESCRIPTION("Generic copyarea (sys-to-sys)");
369 : MODULE_LICENSE("GPL");
370 :
|