Branch data Line data Source code
1 : : /*
2 : : * Generic function for frame buffer with packed pixels of any depth.
3 : : *
4 : : * Copyright (C) 1999-2005 James Simmons <jsimmons@www.infradead.org>
5 : : *
6 : : * This file is subject to the terms and conditions of the GNU General Public
7 : : * License. See the file COPYING in the main directory of this archive for
8 : : * more details.
9 : : *
10 : : * NOTES:
11 : : *
12 : : * This is for cfb packed pixels. Iplan and such are incorporated in the
13 : : * drivers that need them.
14 : : *
15 : : * FIXME
16 : : *
17 : : * Also need to add code to deal with cards endians that are different than
18 : : * the native cpu endians. I also need to deal with MSB position in the word.
19 : : *
20 : : * The two functions or copying forward and backward could be split up like
21 : : * the ones for filling, i.e. in aligned and unaligned versions. This would
22 : : * help moving some redundant computations and branches out of the loop, too.
23 : : */
24 : :
25 : : #include <linux/module.h>
26 : : #include <linux/kernel.h>
27 : : #include <linux/string.h>
28 : : #include <linux/fb.h>
29 : : #include <asm/types.h>
30 : : #include <asm/io.h>
31 : : #include "fb_draw.h"
32 : :
33 : : #if BITS_PER_LONG == 32
34 : : # define FB_WRITEL fb_writel
35 : : # define FB_READL fb_readl
36 : : #else
37 : : # define FB_WRITEL fb_writeq
38 : : # define FB_READL fb_readq
39 : : #endif
40 : :
41 : : /*
42 : : * Generic bitwise copy algorithm
43 : : */
44 : :
45 : : static void
46 : 0 : bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
47 : : const unsigned long __iomem *src, int src_idx, int bits,
48 : : unsigned n, u32 bswapmask)
49 : : {
50 : : unsigned long first, last;
51 : 0 : int const shift = dst_idx-src_idx;
52 : : int left, right;
53 : :
54 : 0 : first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
55 : 0 : last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
56 : :
57 [ # # ]: 0 : if (!shift) {
58 : : // Same alignment for source and dest
59 : :
60 [ # # ]: 0 : if (dst_idx+n <= bits) {
61 : : // Single word
62 [ # # ]: 0 : if (last)
63 : 0 : first &= last;
64 : 0 : FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
65 : : } else {
66 : : // Multiple destination words
67 : :
68 : : // Leading bits
69 [ # # ]: 0 : if (first != ~0UL) {
70 : 0 : FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
71 : 0 : dst++;
72 : 0 : src++;
73 : 0 : n -= bits - dst_idx;
74 : : }
75 : :
76 : : // Main chunk
77 : 0 : n /= bits;
78 [ # # ]: 0 : while (n >= 8) {
79 : 0 : FB_WRITEL(FB_READL(src++), dst++);
80 : 0 : FB_WRITEL(FB_READL(src++), dst++);
81 : 0 : FB_WRITEL(FB_READL(src++), dst++);
82 : 0 : FB_WRITEL(FB_READL(src++), dst++);
83 : 0 : FB_WRITEL(FB_READL(src++), dst++);
84 : 0 : FB_WRITEL(FB_READL(src++), dst++);
85 : 0 : FB_WRITEL(FB_READL(src++), dst++);
86 : 0 : FB_WRITEL(FB_READL(src++), dst++);
87 : 0 : n -= 8;
88 : : }
89 [ # # ]: 0 : while (n--)
90 : 0 : FB_WRITEL(FB_READL(src++), dst++);
91 : :
92 : : // Trailing bits
93 [ # # ]: 0 : if (last)
94 : 0 : FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
95 : : }
96 : : } else {
97 : : /* Different alignment for source and dest */
98 : : unsigned long d0, d1;
99 : : int m;
100 : :
101 : 0 : right = shift & (bits - 1);
102 : 0 : left = -shift & (bits - 1);
103 : 0 : bswapmask &= shift;
104 : :
105 [ # # ]: 0 : if (dst_idx+n <= bits) {
106 : : // Single destination word
107 [ # # ]: 0 : if (last)
108 : 0 : first &= last;
109 : 0 : d0 = FB_READL(src);
110 : : d0 = fb_rev_pixels_in_long(d0, bswapmask);
111 [ # # ]: 0 : if (shift > 0) {
112 : : // Single source word
113 : 0 : d0 >>= right;
114 [ # # ]: 0 : } else if (src_idx+n <= bits) {
115 : : // Single source word
116 : 0 : d0 <<= left;
117 : : } else {
118 : : // 2 source words
119 : 0 : d1 = FB_READL(src + 1);
120 : : d1 = fb_rev_pixels_in_long(d1, bswapmask);
121 : 0 : d0 = d0<<left | d1>>right;
122 : : }
123 : : d0 = fb_rev_pixels_in_long(d0, bswapmask);
124 : 0 : FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
125 : : } else {
126 : : // Multiple destination words
127 : : /** We must always remember the last value read, because in case
128 : : SRC and DST overlap bitwise (e.g. when moving just one pixel in
129 : : 1bpp), we always collect one full long for DST and that might
130 : : overlap with the current long from SRC. We store this value in
131 : : 'd0'. */
132 : 0 : d0 = FB_READL(src++);
133 : : d0 = fb_rev_pixels_in_long(d0, bswapmask);
134 : : // Leading bits
135 [ # # ]: 0 : if (shift > 0) {
136 : : // Single source word
137 : : d1 = d0;
138 : 0 : d0 >>= right;
139 : 0 : dst++;
140 : 0 : n -= bits - dst_idx;
141 : : } else {
142 : : // 2 source words
143 : 0 : d1 = FB_READL(src++);
144 : : d1 = fb_rev_pixels_in_long(d1, bswapmask);
145 : :
146 : 0 : d0 = d0<<left | d1>>right;
147 : 0 : dst++;
148 : 0 : n -= bits - dst_idx;
149 : : }
150 : : d0 = fb_rev_pixels_in_long(d0, bswapmask);
151 : 0 : FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
152 : : d0 = d1;
153 : :
154 : : // Main chunk
155 : 0 : m = n % bits;
156 : 0 : n /= bits;
157 [ # # ]: 0 : while ((n >= 4) && !bswapmask) {
158 : 0 : d1 = FB_READL(src++);
159 : 0 : FB_WRITEL(d0 << left | d1 >> right, dst++);
160 : : d0 = d1;
161 : 0 : d1 = FB_READL(src++);
162 : 0 : FB_WRITEL(d0 << left | d1 >> right, dst++);
163 : : d0 = d1;
164 : 0 : d1 = FB_READL(src++);
165 : 0 : FB_WRITEL(d0 << left | d1 >> right, dst++);
166 : : d0 = d1;
167 : 0 : d1 = FB_READL(src++);
168 : 0 : FB_WRITEL(d0 << left | d1 >> right, dst++);
169 : : d0 = d1;
170 : 0 : n -= 4;
171 : : }
172 [ # # ]: 0 : while (n--) {
173 : 0 : d1 = FB_READL(src++);
174 : : d1 = fb_rev_pixels_in_long(d1, bswapmask);
175 : 0 : d0 = d0 << left | d1 >> right;
176 : : d0 = fb_rev_pixels_in_long(d0, bswapmask);
177 : 0 : FB_WRITEL(d0, dst++);
178 : : d0 = d1;
179 : : }
180 : :
181 : : // Trailing bits
182 [ # # ]: 0 : if (last) {
183 [ # # ]: 0 : if (m <= right) {
184 : : // Single source word
185 : 0 : d0 <<= left;
186 : : } else {
187 : : // 2 source words
188 : 0 : d1 = FB_READL(src);
189 : : d1 = fb_rev_pixels_in_long(d1,
190 : : bswapmask);
191 : 0 : d0 = d0<<left | d1>>right;
192 : : }
193 : : d0 = fb_rev_pixels_in_long(d0, bswapmask);
194 : 0 : FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
195 : : }
196 : : }
197 : : }
198 : 0 : }
199 : :
200 : : /*
201 : : * Generic bitwise copy algorithm, operating backward
202 : : */
203 : :
204 : : static void
205 : 0 : bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
206 : : const unsigned long __iomem *src, int src_idx, int bits,
207 : : unsigned n, u32 bswapmask)
208 : : {
209 : : unsigned long first, last;
210 : : int shift;
211 : :
212 : 0 : dst += (n-1)/bits;
213 : 0 : src += (n-1)/bits;
214 [ # # ]: 0 : if ((n-1) % bits) {
215 : 0 : dst_idx += (n-1) % bits;
216 : 0 : dst += dst_idx >> (ffs(bits) - 1);
217 : 0 : dst_idx &= bits - 1;
218 : 0 : src_idx += (n-1) % bits;
219 : 0 : src += src_idx >> (ffs(bits) - 1);
220 : 0 : src_idx &= bits - 1;
221 : : }
222 : :
223 : 0 : shift = dst_idx-src_idx;
224 : :
225 : 0 : first = fb_shifted_pixels_mask_long(p, bits - 1 - dst_idx, bswapmask);
226 : 0 : last = ~fb_shifted_pixels_mask_long(p, bits - 1 - ((dst_idx-n) % bits),
227 : : bswapmask);
228 : :
229 [ # # ]: 0 : if (!shift) {
230 : : // Same alignment for source and dest
231 : :
232 [ # # ]: 0 : if ((unsigned long)dst_idx+1 >= n) {
233 : : // Single word
234 [ # # ]: 0 : if (last)
235 : 0 : first &= last;
236 : 0 : FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
237 : : } else {
238 : : // Multiple destination words
239 : :
240 : : // Leading bits
241 [ # # ]: 0 : if (first != ~0UL) {
242 : 0 : FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
243 : 0 : dst--;
244 : 0 : src--;
245 : 0 : n -= dst_idx+1;
246 : : }
247 : :
248 : : // Main chunk
249 : 0 : n /= bits;
250 [ # # ]: 0 : while (n >= 8) {
251 : 0 : FB_WRITEL(FB_READL(src--), dst--);
252 : 0 : FB_WRITEL(FB_READL(src--), dst--);
253 : 0 : FB_WRITEL(FB_READL(src--), dst--);
254 : 0 : FB_WRITEL(FB_READL(src--), dst--);
255 : 0 : FB_WRITEL(FB_READL(src--), dst--);
256 : 0 : FB_WRITEL(FB_READL(src--), dst--);
257 : 0 : FB_WRITEL(FB_READL(src--), dst--);
258 : 0 : FB_WRITEL(FB_READL(src--), dst--);
259 : 0 : n -= 8;
260 : : }
261 [ # # ]: 0 : while (n--)
262 : 0 : FB_WRITEL(FB_READL(src--), dst--);
263 : :
264 : : // Trailing bits
265 [ # # ]: 0 : if (last)
266 : 0 : FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
267 : : }
268 : : } else {
269 : : // Different alignment for source and dest
270 : : unsigned long d0, d1;
271 : : int m;
272 : :
273 : 0 : int const left = -shift & (bits-1);
274 : 0 : int const right = shift & (bits-1);
275 : 0 : bswapmask &= shift;
276 : :
277 [ # # ]: 0 : if ((unsigned long)dst_idx+1 >= n) {
278 : : // Single destination word
279 [ # # ]: 0 : if (last)
280 : 0 : first &= last;
281 : 0 : d0 = FB_READL(src);
282 [ # # ]: 0 : if (shift < 0) {
283 : : // Single source word
284 : 0 : d0 <<= left;
285 [ # # ]: 0 : } else if (1+(unsigned long)src_idx >= n) {
286 : : // Single source word
287 : 0 : d0 >>= right;
288 : : } else {
289 : : // 2 source words
290 : 0 : d1 = FB_READL(src - 1);
291 : : d1 = fb_rev_pixels_in_long(d1, bswapmask);
292 : 0 : d0 = d0>>right | d1<<left;
293 : : }
294 : : d0 = fb_rev_pixels_in_long(d0, bswapmask);
295 : 0 : FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
296 : : } else {
297 : : // Multiple destination words
298 : : /** We must always remember the last value read, because in case
299 : : SRC and DST overlap bitwise (e.g. when moving just one pixel in
300 : : 1bpp), we always collect one full long for DST and that might
301 : : overlap with the current long from SRC. We store this value in
302 : : 'd0'. */
303 : :
304 : 0 : d0 = FB_READL(src--);
305 : : d0 = fb_rev_pixels_in_long(d0, bswapmask);
306 : : // Leading bits
307 [ # # ]: 0 : if (shift < 0) {
308 : : // Single source word
309 : : d1 = d0;
310 : 0 : d0 <<= left;
311 : : } else {
312 : : // 2 source words
313 : 0 : d1 = FB_READL(src--);
314 : : d1 = fb_rev_pixels_in_long(d1, bswapmask);
315 : 0 : d0 = d0>>right | d1<<left;
316 : : }
317 : : d0 = fb_rev_pixels_in_long(d0, bswapmask);
318 : 0 : FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
319 : : d0 = d1;
320 : 0 : dst--;
321 : 0 : n -= dst_idx+1;
322 : :
323 : : // Main chunk
324 : 0 : m = n % bits;
325 : 0 : n /= bits;
326 [ # # ]: 0 : while ((n >= 4) && !bswapmask) {
327 : 0 : d1 = FB_READL(src--);
328 : 0 : FB_WRITEL(d0 >> right | d1 << left, dst--);
329 : : d0 = d1;
330 : 0 : d1 = FB_READL(src--);
331 : 0 : FB_WRITEL(d0 >> right | d1 << left, dst--);
332 : : d0 = d1;
333 : 0 : d1 = FB_READL(src--);
334 : 0 : FB_WRITEL(d0 >> right | d1 << left, dst--);
335 : : d0 = d1;
336 : 0 : d1 = FB_READL(src--);
337 : 0 : FB_WRITEL(d0 >> right | d1 << left, dst--);
338 : : d0 = d1;
339 : 0 : n -= 4;
340 : : }
341 [ # # ]: 0 : while (n--) {
342 : 0 : d1 = FB_READL(src--);
343 : : d1 = fb_rev_pixels_in_long(d1, bswapmask);
344 : 0 : d0 = d0 >> right | d1 << left;
345 : : d0 = fb_rev_pixels_in_long(d0, bswapmask);
346 : 0 : FB_WRITEL(d0, dst--);
347 : : d0 = d1;
348 : : }
349 : :
350 : : // Trailing bits
351 [ # # ]: 0 : if (last) {
352 [ # # ]: 0 : if (m <= left) {
353 : : // Single source word
354 : 0 : d0 >>= right;
355 : : } else {
356 : : // 2 source words
357 : 0 : d1 = FB_READL(src);
358 : : d1 = fb_rev_pixels_in_long(d1,
359 : : bswapmask);
360 : 0 : d0 = d0>>right | d1<<left;
361 : : }
362 : : d0 = fb_rev_pixels_in_long(d0, bswapmask);
363 : 0 : FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
364 : : }
365 : : }
366 : : }
367 : 0 : }
368 : :
369 : 0 : void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
370 : : {
371 : 0 : u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
372 : 0 : u32 height = area->height, width = area->width;
373 : 0 : unsigned long const bits_per_line = p->fix.line_length*8u;
374 : : unsigned long __iomem *dst = NULL, *src = NULL;
375 : : int bits = BITS_PER_LONG, bytes = bits >> 3;
376 : : int dst_idx = 0, src_idx = 0, rev_copy = 0;
377 : : u32 bswapmask = fb_compute_bswapmask(p);
378 : :
379 [ # # ]: 0 : if (p->state != FBINFO_STATE_RUNNING)
380 : 0 : return;
381 : :
382 : : /* if the beginning of the target area might overlap with the end of
383 : : the source area, be have to copy the area reverse. */
384 [ # # ][ # # ]: 0 : if ((dy == sy && dx > sx) || (dy > sy)) {
385 : 0 : dy += height;
386 : 0 : sy += height;
387 : : rev_copy = 1;
388 : : }
389 : :
390 : : // split the base of the framebuffer into a long-aligned address and the
391 : : // index of the first bit
392 : 0 : dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
393 : 0 : dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
394 : : // add offset of source and target area
395 : 0 : dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
396 : 0 : src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
397 : :
398 [ # # ]: 0 : if (p->fbops->fb_sync)
399 : 0 : p->fbops->fb_sync(p);
400 : :
401 [ # # ]: 0 : if (rev_copy) {
402 [ # # ]: 0 : while (height--) {
403 : 0 : dst_idx -= bits_per_line;
404 : 0 : src_idx -= bits_per_line;
405 : 0 : dst += dst_idx >> (ffs(bits) - 1);
406 : 0 : dst_idx &= (bytes - 1);
407 : 0 : src += src_idx >> (ffs(bits) - 1);
408 : 0 : src_idx &= (bytes - 1);
409 : 0 : bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
410 : 0 : width*p->var.bits_per_pixel, bswapmask);
411 : : }
412 : : } else {
413 [ # # ]: 0 : while (height--) {
414 : 0 : dst += dst_idx >> (ffs(bits) - 1);
415 : 0 : dst_idx &= (bytes - 1);
416 : 0 : src += src_idx >> (ffs(bits) - 1);
417 : 0 : src_idx &= (bytes - 1);
418 : 0 : bitcpy(p, dst, dst_idx, src, src_idx, bits,
419 : 0 : width*p->var.bits_per_pixel, bswapmask);
420 : 0 : dst_idx += bits_per_line;
421 : 0 : src_idx += bits_per_line;
422 : : }
423 : : }
424 : : }
425 : :
426 : : EXPORT_SYMBOL(cfb_copyarea);
427 : :
428 : : MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
429 : : MODULE_DESCRIPTION("Generic software accelerated copyarea");
430 : : MODULE_LICENSE("GPL");
431 : :
|