Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
cfbcopyarea.c
Go to the documentation of this file.
1 /*
2  * Generic function for frame buffer with packed pixels of any depth.
3  *
4  * Copyright (C) 1999-2005 James Simmons <[email protected]>
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 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  int const shift = dst_idx-src_idx;
52  int left, right;
53 
54  first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
55  last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
56 
57  if (!shift) {
58  // Same alignment for source and dest
59 
60  if (dst_idx+n <= bits) {
61  // Single word
62  if (last)
63  first &= last;
64  FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
65  } else {
66  // Multiple destination words
67 
68  // Leading bits
69  if (first != ~0UL) {
70  FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
71  dst++;
72  src++;
73  n -= bits - dst_idx;
74  }
75 
76  // Main chunk
77  n /= bits;
78  while (n >= 8) {
79  FB_WRITEL(FB_READL(src++), dst++);
80  FB_WRITEL(FB_READL(src++), dst++);
81  FB_WRITEL(FB_READL(src++), dst++);
82  FB_WRITEL(FB_READL(src++), dst++);
83  FB_WRITEL(FB_READL(src++), dst++);
84  FB_WRITEL(FB_READL(src++), dst++);
85  FB_WRITEL(FB_READL(src++), dst++);
86  FB_WRITEL(FB_READL(src++), dst++);
87  n -= 8;
88  }
89  while (n--)
90  FB_WRITEL(FB_READL(src++), dst++);
91 
92  // Trailing bits
93  if (last)
94  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  right = shift & (bits - 1);
102  left = -shift & (bits - 1);
103  bswapmask &= shift;
104 
105  if (dst_idx+n <= bits) {
106  // Single destination word
107  if (last)
108  first &= last;
109  d0 = FB_READL(src);
110  d0 = fb_rev_pixels_in_long(d0, bswapmask);
111  if (shift > 0) {
112  // Single source word
113  d0 >>= right;
114  } else if (src_idx+n <= bits) {
115  // Single source word
116  d0 <<= left;
117  } else {
118  // 2 source words
119  d1 = FB_READL(src + 1);
120  d1 = fb_rev_pixels_in_long(d1, bswapmask);
121  d0 = d0<<left | d1>>right;
122  }
123  d0 = fb_rev_pixels_in_long(d0, bswapmask);
124  FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
125  } else {
126  // Multiple destination words
132  d0 = FB_READL(src++);
133  d0 = fb_rev_pixels_in_long(d0, bswapmask);
134  // Leading bits
135  if (shift > 0) {
136  // Single source word
137  d1 = d0;
138  d0 >>= right;
139  dst++;
140  n -= bits - dst_idx;
141  } else {
142  // 2 source words
143  d1 = FB_READL(src++);
144  d1 = fb_rev_pixels_in_long(d1, bswapmask);
145 
146  d0 = d0<<left | d1>>right;
147  dst++;
148  n -= bits - dst_idx;
149  }
150  d0 = fb_rev_pixels_in_long(d0, bswapmask);
151  FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
152  d0 = d1;
153 
154  // Main chunk
155  m = n % bits;
156  n /= bits;
157  while ((n >= 4) && !bswapmask) {
158  d1 = FB_READL(src++);
159  FB_WRITEL(d0 << left | d1 >> right, dst++);
160  d0 = d1;
161  d1 = FB_READL(src++);
162  FB_WRITEL(d0 << left | d1 >> right, dst++);
163  d0 = d1;
164  d1 = FB_READL(src++);
165  FB_WRITEL(d0 << left | d1 >> right, dst++);
166  d0 = d1;
167  d1 = FB_READL(src++);
168  FB_WRITEL(d0 << left | d1 >> right, dst++);
169  d0 = d1;
170  n -= 4;
171  }
172  while (n--) {
173  d1 = FB_READL(src++);
174  d1 = fb_rev_pixels_in_long(d1, bswapmask);
175  d0 = d0 << left | d1 >> right;
176  d0 = fb_rev_pixels_in_long(d0, bswapmask);
177  FB_WRITEL(d0, dst++);
178  d0 = d1;
179  }
180 
181  // Trailing bits
182  if (last) {
183  if (m <= right) {
184  // Single source word
185  d0 <<= left;
186  } else {
187  // 2 source words
188  d1 = FB_READL(src);
189  d1 = fb_rev_pixels_in_long(d1,
190  bswapmask);
191  d0 = d0<<left | d1>>right;
192  }
193  d0 = fb_rev_pixels_in_long(d0, bswapmask);
194  FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
195  }
196  }
197  }
198 }
199 
200  /*
201  * Generic bitwise copy algorithm, operating backward
202  */
203 
204 static void
205 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  dst += (n-1)/bits;
213  src += (n-1)/bits;
214  if ((n-1) % bits) {
215  dst_idx += (n-1) % bits;
216  dst += dst_idx >> (ffs(bits) - 1);
217  dst_idx &= bits - 1;
218  src_idx += (n-1) % bits;
219  src += src_idx >> (ffs(bits) - 1);
220  src_idx &= bits - 1;
221  }
222 
223  shift = dst_idx-src_idx;
224 
225  first = fb_shifted_pixels_mask_long(p, bits - 1 - dst_idx, bswapmask);
226  last = ~fb_shifted_pixels_mask_long(p, bits - 1 - ((dst_idx-n) % bits),
227  bswapmask);
228 
229  if (!shift) {
230  // Same alignment for source and dest
231 
232  if ((unsigned long)dst_idx+1 >= n) {
233  // Single word
234  if (last)
235  first &= last;
236  FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
237  } else {
238  // Multiple destination words
239 
240  // Leading bits
241  if (first != ~0UL) {
242  FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
243  dst--;
244  src--;
245  n -= dst_idx+1;
246  }
247 
248  // Main chunk
249  n /= bits;
250  while (n >= 8) {
251  FB_WRITEL(FB_READL(src--), dst--);
252  FB_WRITEL(FB_READL(src--), dst--);
253  FB_WRITEL(FB_READL(src--), dst--);
254  FB_WRITEL(FB_READL(src--), dst--);
255  FB_WRITEL(FB_READL(src--), dst--);
256  FB_WRITEL(FB_READL(src--), dst--);
257  FB_WRITEL(FB_READL(src--), dst--);
258  FB_WRITEL(FB_READL(src--), dst--);
259  n -= 8;
260  }
261  while (n--)
262  FB_WRITEL(FB_READL(src--), dst--);
263 
264  // Trailing bits
265  if (last)
266  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  int const left = -shift & (bits-1);
274  int const right = shift & (bits-1);
275  bswapmask &= shift;
276 
277  if ((unsigned long)dst_idx+1 >= n) {
278  // Single destination word
279  if (last)
280  first &= last;
281  d0 = FB_READL(src);
282  if (shift < 0) {
283  // Single source word
284  d0 <<= left;
285  } else if (1+(unsigned long)src_idx >= n) {
286  // Single source word
287  d0 >>= right;
288  } else {
289  // 2 source words
290  d1 = FB_READL(src - 1);
291  d1 = fb_rev_pixels_in_long(d1, bswapmask);
292  d0 = d0>>right | d1<<left;
293  }
294  d0 = fb_rev_pixels_in_long(d0, bswapmask);
295  FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
296  } else {
297  // Multiple destination words
304  d0 = FB_READL(src--);
305  d0 = fb_rev_pixels_in_long(d0, bswapmask);
306  // Leading bits
307  if (shift < 0) {
308  // Single source word
309  d1 = d0;
310  d0 <<= left;
311  } else {
312  // 2 source words
313  d1 = FB_READL(src--);
314  d1 = fb_rev_pixels_in_long(d1, bswapmask);
315  d0 = d0>>right | d1<<left;
316  }
317  d0 = fb_rev_pixels_in_long(d0, bswapmask);
318  FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
319  d0 = d1;
320  dst--;
321  n -= dst_idx+1;
322 
323  // Main chunk
324  m = n % bits;
325  n /= bits;
326  while ((n >= 4) && !bswapmask) {
327  d1 = FB_READL(src--);
328  FB_WRITEL(d0 >> right | d1 << left, dst--);
329  d0 = d1;
330  d1 = FB_READL(src--);
331  FB_WRITEL(d0 >> right | d1 << left, dst--);
332  d0 = d1;
333  d1 = FB_READL(src--);
334  FB_WRITEL(d0 >> right | d1 << left, dst--);
335  d0 = d1;
336  d1 = FB_READL(src--);
337  FB_WRITEL(d0 >> right | d1 << left, dst--);
338  d0 = d1;
339  n -= 4;
340  }
341  while (n--) {
342  d1 = FB_READL(src--);
343  d1 = fb_rev_pixels_in_long(d1, bswapmask);
344  d0 = d0 >> right | d1 << left;
345  d0 = fb_rev_pixels_in_long(d0, bswapmask);
346  FB_WRITEL(d0, dst--);
347  d0 = d1;
348  }
349 
350  // Trailing bits
351  if (last) {
352  if (m <= left) {
353  // Single source word
354  d0 >>= right;
355  } else {
356  // 2 source words
357  d1 = FB_READL(src);
358  d1 = fb_rev_pixels_in_long(d1,
359  bswapmask);
360  d0 = d0>>right | d1<<left;
361  }
362  d0 = fb_rev_pixels_in_long(d0, bswapmask);
363  FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
364  }
365  }
366  }
367 }
368 
369 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
370 {
371  u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
372  u32 height = area->height, width = area->width;
373  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  if (p->state != FBINFO_STATE_RUNNING)
380  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  if ((dy == sy && dx > sx) || (dy > sy)) {
385  dy += height;
386  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  dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
393  dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
394  // add offset of source and target area
395  dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
396  src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
397 
398  if (p->fbops->fb_sync)
399  p->fbops->fb_sync(p);
400 
401  if (rev_copy) {
402  while (height--) {
403  dst_idx -= bits_per_line;
404  src_idx -= bits_per_line;
405  dst += dst_idx >> (ffs(bits) - 1);
406  dst_idx &= (bytes - 1);
407  src += src_idx >> (ffs(bits) - 1);
408  src_idx &= (bytes - 1);
409  bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
410  width*p->var.bits_per_pixel, bswapmask);
411  }
412  } else {
413  while (height--) {
414  dst += dst_idx >> (ffs(bits) - 1);
415  dst_idx &= (bytes - 1);
416  src += src_idx >> (ffs(bits) - 1);
417  src_idx &= (bytes - 1);
418  bitcpy(p, dst, dst_idx, src, src_idx, bits,
419  width*p->var.bits_per_pixel, bswapmask);
420  dst_idx += bits_per_line;
421  src_idx += bits_per_line;
422  }
423  }
424 }
425 
427 
428 MODULE_AUTHOR("James Simmons <[email protected]>");
429 MODULE_DESCRIPTION("Generic software accelerated copyarea");
430 MODULE_LICENSE("GPL");
431