Merge tag 'powerpc-6.6-6' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc...
[platform/kernel/linux-starfive.git] / drivers / video / fbdev / core / 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         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         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                         if (!first)
328                                 FB_WRITEL(d0, dst);
329                         else
330                                 FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
331                         d0 = d1;
332                         dst--;
333                         n -= dst_idx+1;
334
335                         // Main chunk
336                         m = n % bits;
337                         n /= bits;
338                         while ((n >= 4) && !bswapmask) {
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                                 d1 = FB_READL(src--);
349                                 FB_WRITEL(d0 << left | d1 >> right, dst--);
350                                 d0 = d1;
351                                 n -= 4;
352                         }
353                         while (n--) {
354                                 d1 = FB_READL(src--);
355                                 d1 = fb_rev_pixels_in_long(d1, bswapmask);
356                                 d0 = d0 << left | d1 >> right;
357                                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
358                                 FB_WRITEL(d0, dst--);
359                                 d0 = d1;
360                         }
361
362                         // Trailing bits
363                         if (m) {
364                                 if (m <= bits - left) {
365                                         // Single source word
366                                         d0 <<= left;
367                                 } else {
368                                         // 2 source words
369                                         d1 = FB_READL(src);
370                                         d1 = fb_rev_pixels_in_long(d1,
371                                                                 bswapmask);
372                                         d0 = d0 << left | d1 >> right;
373                                 }
374                                 d0 = fb_rev_pixels_in_long(d0, bswapmask);
375                                 FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
376                         }
377                 }
378         }
379 }
380
381 void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
382 {
383         u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
384         u32 height = area->height, width = area->width;
385         unsigned int const bits_per_line = p->fix.line_length * 8u;
386         unsigned long __iomem *base = NULL;
387         int bits = BITS_PER_LONG, bytes = bits >> 3;
388         unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
389         u32 bswapmask = fb_compute_bswapmask(p);
390
391         if (p->state != FBINFO_STATE_RUNNING)
392                 return;
393
394         /* if the beginning of the target area might overlap with the end of
395         the source area, be have to copy the area reverse. */
396         if ((dy == sy && dx > sx) || (dy > sy)) {
397                 dy += height;
398                 sy += height;
399                 rev_copy = 1;
400         }
401
402         // split the base of the framebuffer into a long-aligned address and the
403         // index of the first bit
404         base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
405         dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
406         // add offset of source and target area
407         dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
408         src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
409
410         if (p->fbops->fb_sync)
411                 p->fbops->fb_sync(p);
412
413         if (rev_copy) {
414                 while (height--) {
415                         dst_idx -= bits_per_line;
416                         src_idx -= bits_per_line;
417                         bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
418                                 base + (src_idx / bits), src_idx % bits, bits,
419                                 width*p->var.bits_per_pixel, bswapmask);
420                 }
421         } else {
422                 while (height--) {
423                         bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
424                                 base + (src_idx / bits), src_idx % bits, bits,
425                                 width*p->var.bits_per_pixel, bswapmask);
426                         dst_idx += bits_per_line;
427                         src_idx += bits_per_line;
428                 }
429         }
430 }
431
432 EXPORT_SYMBOL(cfb_copyarea);
433
434 MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
435 MODULE_DESCRIPTION("Generic software accelerated copyarea");
436 MODULE_LICENSE("GPL");
437