Merge tag 'v3.10.71' into update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / video / cfbcopyarea.c
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 bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
47 const unsigned long __iomem *src, unsigned src_idx, int bits,
48 unsigned n, u32 bswapmask)
49 {
50 unsigned long first, last;
51 int const shift = dst_idx-src_idx;
52
53 #if 0
54 /*
55 * If you suspect bug in this function, compare it with this simple
56 * memmove implementation.
57 */
58 fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
59 (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
60 return;
61 #endif
62
63 first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
64 last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
65
66 if (!shift) {
67 // Same alignment for source and dest
68
69 if (dst_idx+n <= bits) {
70 // Single word
71 if (last)
72 first &= last;
73 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
74 } else {
75 // Multiple destination words
76
77 // Leading bits
78 if (first != ~0UL) {
79 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
80 dst++;
81 src++;
82 n -= bits - dst_idx;
83 }
84
85 // Main chunk
86 n /= bits;
87 while (n >= 8) {
88 FB_WRITEL(FB_READL(src++), dst++);
89 FB_WRITEL(FB_READL(src++), dst++);
90 FB_WRITEL(FB_READL(src++), dst++);
91 FB_WRITEL(FB_READL(src++), dst++);
92 FB_WRITEL(FB_READL(src++), dst++);
93 FB_WRITEL(FB_READL(src++), dst++);
94 FB_WRITEL(FB_READL(src++), dst++);
95 FB_WRITEL(FB_READL(src++), dst++);
96 n -= 8;
97 }
98 while (n--)
99 FB_WRITEL(FB_READL(src++), dst++);
100
101 // Trailing bits
102 if (last)
103 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
104 }
105 } else {
106 /* Different alignment for source and dest */
107 unsigned long d0, d1;
108 int m;
109
110 int const left = shift & (bits - 1);
111 int const right = -shift & (bits - 1);
112
113 if (dst_idx+n <= bits) {
114 // Single destination word
115 if (last)
116 first &= last;
117 d0 = FB_READL(src);
118 d0 = fb_rev_pixels_in_long(d0, bswapmask);
119 if (shift > 0) {
120 // Single source word
121 d0 <<= left;
122 } else if (src_idx+n <= bits) {
123 // Single source word
124 d0 >>= right;
125 } else {
126 // 2 source words
127 d1 = FB_READL(src + 1);
128 d1 = fb_rev_pixels_in_long(d1, bswapmask);
129 d0 = d0 >> right | d1 << left;
130 }
131 d0 = fb_rev_pixels_in_long(d0, bswapmask);
132 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
133 } else {
134 // Multiple destination words
135 /** We must always remember the last value read, because in case
136 SRC and DST overlap bitwise (e.g. when moving just one pixel in
137 1bpp), we always collect one full long for DST and that might
138 overlap with the current long from SRC. We store this value in
139 'd0'. */
140 d0 = FB_READL(src++);
141 d0 = fb_rev_pixels_in_long(d0, bswapmask);
142 // Leading bits
143 if (shift > 0) {
144 // Single source word
145 d1 = d0;
146 d0 <<= left;
147 n -= bits - dst_idx;
148 } else {
149 // 2 source words
150 d1 = FB_READL(src++);
151 d1 = fb_rev_pixels_in_long(d1, bswapmask);
152
153 d0 = d0 >> right | d1 << left;
154 n -= bits - dst_idx;
155 }
156 d0 = fb_rev_pixels_in_long(d0, bswapmask);
157 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
158 d0 = d1;
159 dst++;
160
161 // Main chunk
162 m = n % bits;
163 n /= bits;
164 while ((n >= 4) && !bswapmask) {
165 d1 = FB_READL(src++);
166 FB_WRITEL(d0 >> right | d1 << left, dst++);
167 d0 = d1;
168 d1 = FB_READL(src++);
169 FB_WRITEL(d0 >> right | d1 << left, dst++);
170 d0 = d1;
171 d1 = FB_READL(src++);
172 FB_WRITEL(d0 >> right | d1 << left, dst++);
173 d0 = d1;
174 d1 = FB_READL(src++);
175 FB_WRITEL(d0 >> right | d1 << left, dst++);
176 d0 = d1;
177 n -= 4;
178 }
179 while (n--) {
180 d1 = FB_READL(src++);
181 d1 = fb_rev_pixels_in_long(d1, bswapmask);
182 d0 = d0 >> right | d1 << left;
183 d0 = fb_rev_pixels_in_long(d0, bswapmask);
184 FB_WRITEL(d0, dst++);
185 d0 = d1;
186 }
187
188 // Trailing bits
189 if (m) {
190 if (m <= bits - right) {
191 // Single source word
192 d0 >>= right;
193 } else {
194 // 2 source words
195 d1 = FB_READL(src);
196 d1 = fb_rev_pixels_in_long(d1,
197 bswapmask);
198 d0 = d0 >> right | d1 << left;
199 }
200 d0 = fb_rev_pixels_in_long(d0, bswapmask);
201 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
202 }
203 }
204 }
205 }
206
207 /*
208 * Generic bitwise copy algorithm, operating backward
209 */
210
211 static void
212 bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
213 const unsigned long __iomem *src, unsigned src_idx, int bits,
214 unsigned n, u32 bswapmask)
215 {
216 unsigned long first, last;
217 int shift;
218
219 #if 0
220 /*
221 * If you suspect bug in this function, compare it with this simple
222 * memmove implementation.
223 */
224 fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
225 (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
226 return;
227 #endif
228
229 dst += (dst_idx + n - 1) / bits;
230 src += (src_idx + n - 1) / bits;
231 dst_idx = (dst_idx + n - 1) % bits;
232 src_idx = (src_idx + n - 1) % bits;
233
234 shift = dst_idx-src_idx;
235
236 first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask);
237 last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask);
238
239 if (!shift) {
240 // Same alignment for source and dest
241
242 if ((unsigned long)dst_idx+1 >= n) {
243 // Single word
244 if (first)
245 last &= first;
246 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
247 } else {
248 // Multiple destination words
249
250 // Leading bits
251 if (first) {
252 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
253 dst--;
254 src--;
255 n -= dst_idx+1;
256 }
257
258 // Main chunk
259 n /= bits;
260 while (n >= 8) {
261 FB_WRITEL(FB_READL(src--), dst--);
262 FB_WRITEL(FB_READL(src--), dst--);
263 FB_WRITEL(FB_READL(src--), dst--);
264 FB_WRITEL(FB_READL(src--), dst--);
265 FB_WRITEL(FB_READL(src--), dst--);
266 FB_WRITEL(FB_READL(src--), dst--);
267 FB_WRITEL(FB_READL(src--), dst--);
268 FB_WRITEL(FB_READL(src--), dst--);
269 n -= 8;
270 }
271 while (n--)
272 FB_WRITEL(FB_READL(src--), dst--);
273
274 // Trailing bits
275 if (last != -1UL)
276 FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
277 }
278 } else {
279 // Different alignment for source and dest
280 unsigned long d0, d1;
281 int m;
282
283 int const left = shift & (bits-1);
284 int const right = -shift & (bits-1);
285
286 if ((unsigned long)dst_idx+1 >= n) {
287 // Single destination word
288 if (first)
289 last &= first;
290 d0 = FB_READL(src);
291 if (shift < 0) {
292 // Single source word
293 d0 >>= right;
294 } else if (1+(unsigned long)src_idx >= n) {
295 // Single source word
296 d0 <<= left;
297 } else {
298 // 2 source words
299 d1 = FB_READL(src - 1);
300 d1 = fb_rev_pixels_in_long(d1, bswapmask);
301 d0 = d0 << left | d1 >> right;
302 }
303 d0 = fb_rev_pixels_in_long(d0, bswapmask);
304 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
305 } else {
306 // Multiple destination words
307 /** We must always remember the last value read, because in case
308 SRC and DST overlap bitwise (e.g. when moving just one pixel in
309 1bpp), we always collect one full long for DST and that might
310 overlap with the current long from SRC. We store this value in
311 'd0'. */
312
313 d0 = FB_READL(src--);
314 d0 = fb_rev_pixels_in_long(d0, bswapmask);
315 // Leading bits
316 if (shift < 0) {
317 // Single source word
318 d1 = d0;
319 d0 >>= right;
320 } else {
321 // 2 source words
322 d1 = FB_READL(src--);
323 d1 = fb_rev_pixels_in_long(d1, bswapmask);
324 d0 = d0 << left | d1 >> right;
325 }
326 d0 = fb_rev_pixels_in_long(d0, bswapmask);
327 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
328 d0 = d1;
329 dst--;
330 n -= dst_idx+1;
331
332 // Main chunk
333 m = n % bits;
334 n /= bits;
335 while ((n >= 4) && !bswapmask) {
336 d1 = FB_READL(src--);
337 FB_WRITEL(d0 << left | d1 >> right, dst--);
338 d0 = d1;
339 d1 = FB_READL(src--);
340 FB_WRITEL(d0 << left | d1 >> right, dst--);
341 d0 = d1;
342 d1 = FB_READL(src--);
343 FB_WRITEL(d0 << left | d1 >> right, dst--);
344 d0 = d1;
345 d1 = FB_READL(src--);
346 FB_WRITEL(d0 << left | d1 >> right, dst--);
347 d0 = d1;
348 n -= 4;
349 }
350 while (n--) {
351 d1 = FB_READL(src--);
352 d1 = fb_rev_pixels_in_long(d1, bswapmask);
353 d0 = d0 << left | d1 >> right;
354 d0 = fb_rev_pixels_in_long(d0, bswapmask);
355 FB_WRITEL(d0, dst--);
356 d0 = d1;
357 }
358
359 // Trailing bits
360 if (m) {
361 if (m <= bits - left) {
362 // Single source word
363 d0 <<= left;
364 } else {
365 // 2 source words
366 d1 = FB_READL(src);
367 d1 = fb_rev_pixels_in_long(d1,
368 bswapmask);
369 d0 = d0 << left | d1 >> right;
370 }
371 d0 = fb_rev_pixels_in_long(d0, bswapmask);
372 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
373 }
374 }
375 }
376 }
377
378 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
379 {
380 u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
381 u32 height = area->height, width = area->width;
382 unsigned long const bits_per_line = p->fix.line_length*8u;
383 unsigned long __iomem *base = NULL;
384 int bits = BITS_PER_LONG, bytes = bits >> 3;
385 unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
386 u32 bswapmask = fb_compute_bswapmask(p);
387
388 if (p->state != FBINFO_STATE_RUNNING)
389 return;
390
391 /* if the beginning of the target area might overlap with the end of
392 the source area, be have to copy the area reverse. */
393 if ((dy == sy && dx > sx) || (dy > sy)) {
394 dy += height;
395 sy += height;
396 rev_copy = 1;
397 }
398
399 // split the base of the framebuffer into a long-aligned address and the
400 // index of the first bit
401 base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
402 dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
403 // add offset of source and target area
404 dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
405 src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
406
407 if (p->fbops->fb_sync)
408 p->fbops->fb_sync(p);
409
410 if (rev_copy) {
411 while (height--) {
412 dst_idx -= bits_per_line;
413 src_idx -= bits_per_line;
414 bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
415 base + (src_idx / bits), src_idx % bits, bits,
416 width*p->var.bits_per_pixel, bswapmask);
417 }
418 } else {
419 while (height--) {
420 bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
421 base + (src_idx / bits), src_idx % bits, bits,
422 width*p->var.bits_per_pixel, bswapmask);
423 dst_idx += bits_per_line;
424 src_idx += bits_per_line;
425 }
426 }
427 }
428
429 EXPORT_SYMBOL(cfb_copyarea);
430
431 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
432 MODULE_DESCRIPTION("Generic software accelerated copyarea");
433 MODULE_LICENSE("GPL");
434